TypeScript如何基于不同参数的子类动态创建类实例?
解决TypeScript动态创建类实例的类型不匹配问题
你的问题核心在于类标识字符串与对应模型类型没有建立强关联,导致TypeScript无法推断出createMenuElement方法中type参数对应的构造函数需要什么类型的model,同时也没法在调用时校验model与type是否匹配。
解决方案步骤
1. 建立类与模型的类型映射
首先定义一个类型,明确每个类标识对应的构造函数及其所需的模型参数类型:
// 定义每个类标识对应的构造函数和模型类型 type ClassModelMap = { Base: { Class: typeof Base; Model: BaseModel; }; Sub: { Class: typeof Sub; Model: SubModel; }; Sub2: { Class: typeof Sub2; Model: SubModel2; }; };
2. 重构Store的类型约束
让Store严格遵循上面的类型映射,确保每个key对应的类和模型类型一一对应:
const Store: { [K in keyof ClassModelMap]: ClassModelMap[K]['Class'] } = { Base: Base, Sub: Sub, Sub2: Sub2, };
3. 修改createMenuElement方法的泛型逻辑
调整方法的泛型,让type和model基于ClassModelMap建立强关联,这样TypeScript就能自动推断并校验类型:
class DynamicMenuElement { static instance: DynamicMenuElement; public static get() { if (!DynamicMenuElement.instance) { DynamicMenuElement.instance = new DynamicMenuElement(); } return DynamicMenuElement.instance; } // 泛型K绑定到ClassModelMap的key,model必须是对应K的Model类型 public createMenuElement<K extends keyof ClassModelMap>( type: K, model: ClassModelMap[K]['Model'] ) { const Class = Store[type]; if (!Class) { throw new Error(`Cannot find the class type of ${type}`); } return new Class(model) as InstanceType<ClassModelMap[K]['Class']>; } }
修改后的完整代码
interface BaseModel { name: string; age: number; } interface SubModel extends BaseModel { color: string; speed: number; } interface SubModel2 extends BaseModel { shoeSize: number; height: number; } class Base { name = ''; age = 1; constructor(model: BaseModel) { this.name = model.name; this.age = model.age; } } class Sub extends Base { color = ''; speed = 0; constructor(model: SubModel) { super(model); this.color = model.color; this.speed = model.speed; } } class Sub2 extends Base { shoeSize = 0; height = 0; constructor(model: SubModel2) { super(model); this.shoeSize = model.shoeSize; this.height = model.height; } } // 1. 建立类与模型的类型映射 type ClassModelMap = { Base: { Class: typeof Base; Model: BaseModel; }; Sub: { Class: typeof Sub; Model: SubModel; }; Sub2: { Class: typeof Sub2; Model: SubModel2; }; }; // 2. 重构Store的类型约束 const Store: { [K in keyof ClassModelMap]: ClassModelMap[K]['Class'] } = { Base: Base, Sub: Sub, Sub2: Sub2, }; class DynamicMenuElement { static instance: DynamicMenuElement; public static get() { if (!DynamicMenuElement.instance) { DynamicMenuElement.instance = new DynamicMenuElement(); } return DynamicMenuElement.instance; } // 3. 强关联type与model的类型 public createMenuElement<K extends keyof ClassModelMap>( type: K, model: ClassModelMap[K]['Model'] ) { const Class = Store[type]; if (!Class) { throw new Error(`Cannot find the class type of ${type}`); } return new Class(model) as InstanceType<ClassModelMap[K]['Class']>; } } const base = { name: 'John', age: 39 }; const sub = { color: 'green', speed: 100, ...base }; const sub2 = { shoeSize: 10.5, height: 69, ...base }; // 调用示例 const element1 = DynamicMenuElement.get().createMenuElement('Base', base); // 正常 const element2 = DynamicMenuElement.get().createMenuElement('Sub', sub); // 正常 const element3 = DynamicMenuElement.get().createMenuElement('Sub', sub2); // 触发类型错误(符合预期)
效果说明
- 现在
createMenuElement方法中new Class(model)不再报错,TypeScript能明确确认model类型与对应类的构造函数参数匹配。 - 调用时如果传入不匹配的
type和model(比如用'Sub'传入sub2),TypeScript会立即触发类型错误,符合预期校验逻辑。 - 新增子类时,只需要在
ClassModelMap中添加对应的条目,就能自动扩展类型约束,无需修改其他核心逻辑。
内容的提问来源于stack exchange,提问作者VarJohn
相关产品推荐
相关产品推荐

