如何用泛型保留构造函数类型信息?实例类型为何未正确推断?
TypeScript Registry类实例类型推断问题解析
问题场景
以下TypeScript代码实现了Registry类,期望通过泛型关联构造函数与实例类型,但调用getInst('foo')时返回类型为unknown而非预期的Foo:
class Registry<Inst, Ctor extends new (...args: unknown[]) => Inst, T extends Readonly<Record<string, Ctor>>> { constructor(public records: T) { } getCtor<K extends keyof T>(key: K) { return this.records[key] } getInst<K extends keyof T>(key: K) { const ctor = this.records[key] return new ctor() } } class Foo { foo: "foo" } class Bar { bar: "bar" } const registry = new Registry({ foo: Foo, bar: Bar }) const ctor = registry.getCtor('foo') // 类型为 typeof Foo(符合预期) const inst = registry.getInst('foo') // 类型为 unknown(不符合预期)
问题原因
原Registry类的泛型设计存在缺陷:
- 试图用全局泛型参数
Inst和Ctor约束整个T对象,但T中Foo和Bar构造函数的实例类型没有共同的有效超类型(除了unknown),TypeScript因此将Inst推断为unknown。 getCtor直接返回T[K],也就是具体的构造函数类型(比如typeof Foo),所以类型推断正确;但getInst中new ctor()的类型被绑定到全局的Inst参数,最终返回unknown。
本质是全局泛型参数无法适配T中每个构造函数对应的独立实例类型,导致推断失效。
修复方案
修改Registry类的泛型设计,直接基于T本身推导每个键对应的实例类型,不再依赖全局的Inst和Ctor参数:
class Registry<T extends Readonly<Record<string, new (...args: any[]) => any>>> { constructor(public records: T) { } getCtor<K extends keyof T>(key: K): T[K] { return this.records[key] } getInst<K extends keyof T>(key: K): InstanceType<T[K]> { return new this.records[key]() } } class Foo { foo: "foo" } class Bar { bar: "bar" } const registry = new Registry({ foo: Foo, bar: Bar }) const ctor = registry.getCtor('foo') // typeof Foo const inst = registry.getInst('foo') // Foo(类型推断正确) const barInst = registry.getInst('bar') // Bar(类型推断正确)
关键改动说明
- 移除全局泛型参数
Inst和Ctor,仅保留T用于约束构造函数对象。 - 使用TypeScript内置工具类型
InstanceType<T[K]>,直接从T中对应键的构造函数类型推导实例类型,实现精准的类型关联。
内容的提问来源于stack exchange,提问作者kumarchandresh
相关产品推荐
相关产品推荐

