TypeScript中如何解决bindNew重载构造函数类型推断问题?
解决TypeScript中bindNew函数无法推断重载构造函数类型的问题
问题描述
bindNew函数无法正确推断类的重载构造函数类型,仅能识别多个重载版本中的最后一个。调用A(0)时会报错提示需要2个参数,虽然手动显式声明类型可以解决,但需要额外的类型定义,希望找到无需手动声明的方案。
原代码示例
function bindNew<C extends { new(...args: A): T }, A extends any[], T>( klass: C & { new(...args: A): T } ): C & ((...args: A) => T) { return new Proxy(klass, { apply(target, _self, args) { return new (target as any)(...args) } }) as any } class AClass { constructor(a: number) constructor(a: number, b: number) constructor(a: number, b?: number) {} } const A = bindNew(AClass) A(0) // => Error: expects 2 arguments A(0, 0) // 手动声明类型的解决方案(需要额外定义) // interface A extends AClass {} // const A = bindNew(AClass) as typeof AClass & { // (a: number): A // (a: number, b: number): A // }
解决方案
通过扩展类型定义,让bindNew能够提取构造函数的所有重载签名并转换为对应的函数调用签名。我们可以定义一个辅助类型ConstructorToFunction来处理不同数量的重载,然后修改bindNew的泛型参数和返回类型:
// 辅助类型:将构造函数的重载转换为函数重载 type ConstructorToFunction<C> = // 处理4个重载的情况(可根据需要扩展更多) C extends { new(...args: infer A1): infer T1; new(...args: infer A2): infer T2; new(...args: infer A3): infer T3; new(...args: infer A4): infer T4; } ? ((...args: A1) => T1) & ((...args: A2) => T2) & ((...args: A3) => T3) & ((...args: A4) => T4) : // 处理3个重载的情况 C extends { new(...args: infer A1): infer T1; new(...args: infer A2): infer T2; new(...args: infer A3): infer T3; } ? ((...args: A1) => T1) & ((...args: A2) => T2) & ((...args: A3) => T3) : // 处理2个重载的情况 C extends { new(...args: infer A1): infer T1; new(...args: infer A2): infer T2; } ? ((...args: A1) => T1) & ((...args: A2) => T2) : // 处理单个重载的情况 C extends { new(...args: infer A1): infer T1; } ? ((...args: A1) => T1) : never; function bindNew<C extends new (...args: any[]) => any>( klass: C ): C & ConstructorToFunction<C> { return new Proxy(klass, { apply(target, _self, args) { return new (target as any)(...args); } }) as any; } class AClass { constructor(a: number) constructor(a: number, b: number) constructor(a: number, b?: number) {} } const A = bindNew(AClass); A(0); // 正常推断,无报错 A(0, 0); // 正常工作
原理说明
ConstructorToFunction类型通过多层条件判断,匹配构造函数的不同重载数量,将每个构造函数签名转换为对应的函数调用签名。- 修改后的
bindNew函数接受任意构造函数类型C,返回类型为原构造函数类型C与转换后的函数重载类型的交集,既保留了构造函数的特性,又新增了函数调用的重载签名。 - 如果需要支持更多数量的重载,只需在
ConstructorToFunction中继续添加对应的条件分支即可。
内容的提问来源于stack exchange,提问作者Alex Craft
相关产品推荐
相关产品推荐

