打字稿中的默认值和泛型

Sté*_*ret 14 generics default-value typescript

我想创建以下类:

class MyClass<T = {}> {
  constructor(private values: () => Promise<T> = () => Promise.resolve({})) {}
}
Run Code Online (Sandbox Code Playgroud)

当然,编译器会抱怨,因为类型T未知,因此无法为其分配空对象:

Type '() => Promise<{}>' is not assignable to type '() => Promise<T>'.
Run Code Online (Sandbox Code Playgroud)

但是我觉得每次创建一个MyClass具有默认T值的新对象时都给构造函数赋予默认方法是相当“脏”的。

你认为最好的写法是什么?

Tit*_*mir 10

正如您指出的那样,将{}any指定为默认值是不安全的T,因此编译器禁止它。

如果你想强制编译器接受默认值,你可以使用类型断言:

class MyClass<T = {}> {
    constructor(private values: () => Promise<T> = (() => Promise.resolve({})) as any) {}

}
Run Code Online (Sandbox Code Playgroud)

缺点是对于{}不是有效默认值的类型,您将获得无效默认值并且编译器不会就此警告您。

一种不同的方法是使用条件类型和其余参数中的元组和传播表达式来创建一个构造函数签名,该签名根据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;}>
Run Code Online (Sandbox Code Playgroud)

我们仍然使用类型断言将默认值添加到参数中,但该签名不可见,如果{}不是该类型的有效默认值,编译器将强制我们指定默认值。