TypeScript中如何按名称返回对应构造函数类型的类实例
TypeScript 实现根据名称返回对应子类实例的类型推导
我有一组继承自抽象类Car的子类,每个子类包含独有的方法和属性:
export default abstract class Car { public abstract name: string; } class Mercedes extends Car { public name = 'Mercedes'; speed() { return 70; } } class MazdaBusiness extends Car { public name = 'Mazda'; height() { return 12; } } class MazdaSport extends Car { public name = 'Mazda2'; height() { return 14; } }
我写了一个ParkingLot类,其中getCar方法根据传入的名称查找对应车辆实例,但目前返回的是Car类型,无法自动补全子类的方法和属性:
class ParkingLot { public cars: Car[]; constructor () { this.cars = [new MazdaBusiness(), new MazdaSport(), new Mercedes()]; } public getCar(name) { const capitalizeFirstLetter = (string) => string.charAt(0).toUpperCase() + string.slice(1); const car = this.cars.find( (car) => car.name === capitalizeFirstLetter(name) ); return car; } }
请问如何让getCar方法返回对应子类的实例类型?比如传入'Mercedes'时返回Mercedes类型,传入'mazda2'时返回MazdaSport类型,从而获得自动补全并访问子类的所有成员。
解决方案
要实现这个需求,需要通过类型映射和泛型约束让TypeScript根据传入的名称推导出对应的子类类型,具体步骤如下:
1. 定义名称与子类的类型映射
首先创建一个类型,将车辆名称(与子类name属性严格匹配)和对应的子类类型关联:
type CarNameToType = { 'Mercedes': Mercedes; 'Mazda': MazdaBusiness; 'Mazda2': MazdaSport; };
2. 改造ParkingLot的类型定义
将cars数组的类型从Car[]改为所有子类的联合类型数组,同时给getCar方法添加泛型约束,让TypeScript能根据传入的名称推导返回类型:
class ParkingLot { // 用子类联合类型替代Car[],保留所有子类的类型信息 public cars: (Mercedes | MazdaBusiness | MazdaSport)[]; constructor() { this.cars = [new MazdaBusiness(), new MazdaSport(), new Mercedes()]; } // 泛型方法:约束传入名称必须是CarNameToType的键,返回对应子类类型 public getCar<T extends keyof CarNameToType>(name: string): CarNameToType[T] | undefined { // 标准化名称格式:首字母大写,其余保持原样(适配Mazda2这类带数字的名称) const normalizedName = name.charAt(0).toUpperCase() + name.slice(1); // 类型断言:明确告诉TS找到的实例对应泛型T的类型 return this.cars.find(car => car.name === normalizedName) as CarNameToType[T] | undefined; } }
3. 验证类型推导效果
现在调用getCar时,TypeScript会自动根据传入的名称推导返回类型,支持成员自动补全:
const parkingLot = new ParkingLot(); // 返回Mercedes类型,可自动补全speed()方法 const mercedes = parkingLot.getCar('mercedes'); mercedes?.speed(); // 无类型错误,正常提示 // 返回MazdaSport类型,可自动补全height()方法 const mazda2 = parkingLot.getCar('mazda2'); mazda2?.height(); // 无类型错误,正常提示 // 返回MazdaBusiness类型 const mazda = parkingLot.getCar('mazda'); mazda?.height();
补充说明
- 后续新增子类时,只需在
CarNameToType中添加新的键值对,同时更新cars数组的类型(也可以用typeof自动推导数组类型进一步简化) - 名称转换逻辑需确保和子类
name属性完全匹配,避免因大小写或格式不一致导致类型推导失效 - 返回类型包含
undefined是因为find方法可能找不到匹配实例,符合TypeScript严格类型检查要求
内容的提问来源于stack exchange,提问作者xWiZeRx
相关产品推荐
相关产品推荐

