Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default value and generics in typescript

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?

like image 382
Stéphane Veyret Avatar asked Sep 24 '18 09:09

Stéphane Veyret


1 Answers

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.

like image 169
Titian Cernicova-Dragomir Avatar answered Oct 05 '22 19:10

Titian Cernicova-Dragomir