You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用泛型保留构造函数类型信息?实例类型为何未正确推断?

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 03:17:44