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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 12:58:11