我正在尝试使用映射类型和泛型来合成这些重载,
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
答案 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
的实现内部的错误,编译器通常不如C
到StringConstructor
那样聪明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
,因此您必须谨慎执行。这类似于断言。不知道这里是否有聪明的方法来保证类型安全...但是您可能不需要它。
希望有帮助。祝你好运!