如何封装new关键字且不丢失构造类型的泛型参数
TypeScript构造函数传入通用函数时保留自动类型推导的方法
我希望将构造函数及其参数传入一个通用函数,同时返回结果不丢失类型信息,但当前实现的类型推导不符合预期:
const _new = <A, B>(a: A[], b: new (a: A[]) => B) => new b(a) const set = _new([1, 2], Set) // 得到类型 Set<unknown>,但期望是 Set<number>
直接调用构造函数时类型推导正常:
const set = new Set([1, 2]) // 类型为 Set<number>
手动指定泛型可以解决,但我希望实现自动推导:
const set = _new([1, 2], Set<number>) // 类型为 Set<number>
问题原因
问题出在SetConstructor的重载定义上。当两个重载合并后,TypeScript无法正确从数组参数推导泛型T,但单独使用任意一个重载都能得到正确的Set<number>类型:
interface SetConstructor { new <T>(iterable?: Iterable<T> | null): Set<T> } interface SetConstructor { new <T = any>(values?: readonly T[] | null): Set<T> readonly prototype: Set<any> }
解决方案
通过TypeScript内置的ConstructorParameters和InstanceType工具类型,将函数泛型与构造函数的参数、实例类型关联,即可实现自动推导:
通用实现(保持原调用顺序)
const _new = <C extends new (...args: any[]) => any>(arg: ConstructorParameters<C>[0], ctor: C): InstanceType<C> => { return new ctor(arg) } // 测试 const set = _new([1, 2], Set) // 自动推导为 Set<number> const map = _new([[1, 'a'], [2, 'b']], Map) // 自动推导为 Map<number, string>
更灵活的多参数版本
如果需要支持构造函数接收多个参数,可以调整为:
const _new = <C extends new (...args: any[]) => any>(ctor: C, ...args: ConstructorParameters<C>): InstanceType<C> => { return new ctor(...args) } // 测试 const set = _new(Set, [1, 2]) // 自动推导为 Set<number> const date = _new(Date, 2024, 5, 1) // 自动推导为 Date
原理说明
ConstructorParameters<C>:提取构造函数C的参数类型组成元组InstanceType<C>:提取构造函数C的实例类型- 泛型
C extends new (...args: any[]) => any限制传入的必须是构造函数
通过这种方式,TypeScript可以根据传入的构造函数和参数,双向推导正确的实例类型,解决重载导致的推导失效问题。
内容的提问来源于stack exchange,提问作者slikts
相关产品推荐
相关产品推荐

