如何断言 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
。它类似于断言。不确定这里是否有保证类型安全的聪明方法......但你可能不需要它。
希望对您有所帮助。祝你好运!
我正在尝试使用映射类型和泛型来合成这些重载,
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
。它类似于断言。不确定这里是否有保证类型安全的聪明方法......但你可能不需要它。
希望对您有所帮助。祝你好运!