如何断言 TypeScript 泛型类型

How to assert TypeScript Generics type

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

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

要在 调用 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实现中的错误,编译器一般都不够聪明,无法narrow the type of a generic type parameter like C to StringConstructor, and will complain. The easiest way to deal with that is usually to use a single 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()
}

可行,但您必须谨慎实施,因为 return 类型是 any。它类似于断言。不确定这里是否有保证类型安全的聪明方法......但你可能不需要它。


希望对您有所帮助。祝你好运!