Search code examples
typescriptgenericsdefault-value

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?


Solution

  • 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.