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);
关键修改点说明
- 静态name属性字面量类型:给每个
Component子类添加static name = "ClassName" as const,让TypeScript能将类名捕获为字符串字面量类型,这样后续可以通过Lowercase<C[K]['name']>生成小写的键名类型。 - 泛型与返回类型映射:
- 泛型
C定义为Component子类构造函数的元组类型。 - 返回类型使用映射类型
{ [K in keyof C as Lowercase<C[K]['name']>]: InstanceType<C[K]> },将元组中的每个构造函数映射为:键是类名小写,值是该构造函数的实例类型。
- 泛型
- 类型断言匹配:在
reduce函数中,将累加器和获取到的组件断言为对应的返回类型,确保TypeScript认可类型匹配。
这样修改后,调用player.get(Rectangle, Direction)时,返回的p_components会有rectangle和direction两个属性,且各自的类型分别为Rectangle和Direction,智能提示可以正常工作。
内容的提问来源于stack exchange,提问作者DiDi
相关产品推荐
相关产品推荐

