如何声明TypeScript泛型类型

时间:2018-08-22 17:56:24

标签: typescript generics typescript-generics

我正在尝试使用映射类型和泛型来合成这些重载,

function x(_: BooleanConstructor): boolean
function x(_: StringConstructor): string
function x(_: NumberConstructor): number
function x<T>(_: Constructor<T>): T

但是我有很多困难,尤其是这个

我想知道为什么以下代码(open in TypeScript playground)不起作用。

export type Constructor<T> = new (...args: any[]) => T
export type MappedResult<T> =
    T extends Boolean ? boolean :
    T extends Number ? number :
    T extends String ? string :
    T

function make<T, Ctor = Constructor<T>, Result = MappedResult<T>>(ctor: Ctor): Result {
    if (ctor === String) { return '' } // would produce error
    throw new Error()
}

const str = make<String, StringConstructor, string>(String) // string!
const wrongInferenceStr = make(String) // would be {}

我的理解是,T在TypeScript 3.0中更像是新的unknown类型,因此我必须声明其身份,对此有什么办法吗?

更新

使用jcalz的答案,我尝试使用this,但是没有运气。

简而言之,我认为编译器有问题。 Issue here

1 个答案:

答案 0 :(得分:1)

要在调用 make时正确地进行推理,最好是其签名要求尽可能简单的推理。这意味着:为编译器提供最少的决策空间,并使这些决策尽可能简单。例如,只有一个类型参数与ctor参数的类型完全对应,然后使用条件类型来计算输出的相关类型。像这样:

declare function make<C extends Constructor<any>>(ctor: C): 
  MappedResult<C extends Constructor<infer T> ? T : never>;

现在,您得到

const str = make(String); // string

对于make实现内部的错误,编译器通常不如CStringConstructor那样聪明narrow the type of a generic type parameter,会抱怨的处理该问题的最简单方法通常是使用单个overload作为调用方签名,并使实现签名更具宽松性(但类型安全性较低)。例如:

function make<C extends Constructor<any>>(ctor: C): MappedResult<C extends Constructor<infer T> ? T : never>;
function make(ctor: Constructor<any>): any {
    if (ctor === String) { return '' } // no error now
    throw new Error()
}

可以,但是由于返回类型为any,因此您必须谨慎执行。这类似于断言。不知道这里是否有聪明的方法来保证类型安全...但是您可能不需要它。


希望有帮助。祝你好运!