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

TypeScript:如何从动态创建的对象中正确推断子类类型

解决方案

要实现传入子类构造函数后返回带类型提示的对象,需要调整TypeScript的泛型定义和返回类型映射,同时确保类的名称能被TypeScript正确捕获为字面量类型。

修改后的完整代码

class Component {
  // 定义静态name属性,让子类覆盖为字面量类型
  static name: string;
  get name(): string {
    return this.constructor.name;
  }
}

class Direction extends Component {
  static name = "Direction" as const;
  x: number;
  y: number;

  constructor(x: number = 0, y: number = 0) {
    super();
    this.x = x;
    this.y = y;
  }
}

class Rectangle extends Component {
  static name = "Rectangle" as const;
  x: number;
  y: number;
  w: number;
  h: number;

  constructor(x: number, y: number, w: number, h: number) {
    super();
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
  }
}

class Entity {
  components: Map<string, Component>;

  constructor(...components: Component[]) {
    this.components = new Map();
    components.forEach(component => this.add(component));
  }

  // 调整泛型和返回类型,生成对应键值对的类型
  get<C extends Array<new (...args: any[]) => Component>>(
    ...components: C
  ): { [K in keyof C as Lowercase<C[K]['name']>]: InstanceType<C[K]> } {
    return components.reduce((accumulator, component) => {
      const key = component.name.toLowerCase() as Lowercase<C[number]['name']>;
      accumulator[key] = this.components.get(component.name)! as InstanceType<C[number]>;
      return accumulator;
    }, {} as { [K in keyof C as Lowercase<C[K]['name']>]: InstanceType<C[K]> });
  }

  add(component: Component) {
    this.components.set(component.name, component);
  }
}

const player = new Entity(
  new Rectangle(100, 100, 100, 100),
  new Direction(1, 1)
);

const p_components = player.get(Rectangle, Direction);
// 现在智能提示会识别p_components.rectangle为Rectangle类型,p_components.direction为Direction类型
console.log(p_components.rectangle.x);
console.log(p_components.direction.y);

关键修改点说明

  1. 静态name属性字面量类型:给每个Component子类添加static name = "ClassName" as const,让TypeScript能将类名捕获为字符串字面量类型,这样后续可以通过Lowercase<C[K]['name']>生成小写的键名类型。
  2. 泛型与返回类型映射:
    • 泛型C定义为Component子类构造函数的元组类型。
    • 返回类型使用映射类型{ [K in keyof C as Lowercase<C[K]['name']>]: InstanceType<C[K]> },将元组中的每个构造函数映射为:键是类名小写,值是该构造函数的实例类型。
  3. 类型断言匹配:在reduce函数中,将累加器和获取到的组件断言为对应的返回类型,确保TypeScript认可类型匹配。

这样修改后,调用player.get(Rectangle, Direction)时,返回的p_components会有rectangle和direction两个属性,且各自的类型分别为Rectangle和Direction,智能提示可以正常工作。

内容的提问来源于stack exchange,提问作者DiDi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 18:44:58