I'd like to create the following class:
class MyClass<T = {}> {
constructor(private values: () => Promise<T> = () => Promise.resolve({})) {}
}
Of course, compiler complains because type T
is unknown and it is then impossible to assign an empty object to it:
Type '() => Promise<{}>' is not assignable to type '() => Promise<T>'.
But I feel it rather “dirty” to give the default method to constructor each time I create a new MyClass
object with default T
value.
What would you think would be the best way write this?
As you point out it is not safe to specify {}
as a default for any T
and thus the compiler forbids it.
If you want to force the compiler to accept the default you can just use a type assertion:
class MyClass<T = {}> {
constructor(private values: () => Promise<T> = (() => Promise.resolve({})) as any) {}
}
The disadvantage is that for types where {}
is not a valid default you will get an invalid default and the compiler will not warn you about it.
A different approach would be to use conditional types, and Tuples in rest parameters and spread expressions to create a constructor signature that varies based on the actual type of T
.
class MyClass<T = {}> {
constructor(... values: {} extends T ? [(() => Promise<T>)?]: [() => Promise<T>])
constructor(private values: () => Promise<T> = (() => Promise.resolve({})) as any) {}
}
new MyClass<{}>()
new MyClass<{a?: number}>() // ok {} would be default
new MyClass<{a: number}>() // error {} would not be valid
new MyClass(() => Promise.resolve({}))
new MyClass(() => Promise.resolve({ a: 0})) // ok MyClass<{a: number;}>
new MyClass<{a: number;}>(() => Promise.resolve({ a: 0})) // ok MyClass<{a: number;}>
We still use a type assertion to get the default into the parameter, but that signature is not visible, and the compiler will force us to specify a default if {}
is not a valid default for the type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With