TypeScript中正确声明继承抽象基类的扩展类类型的方法
问题场景
定义抽象基类BaseClass以及多个继承并实现其抽象方法的扩展子类,需要在函数中实例化扩展类后存入普通JS对象,初始实现代码如下:
abstract class BaseClass { abstract fun(): void; } class ExtendedClassA extends BaseClass { fun(): void { console.log("Class A"); } } class ExtendedClassB extends BaseClass { fun(): void { console.log("Class B"); } } type ExtendedObjects = { [name: string]: BaseClass }; type ExtendedClasses = { [name: string]: BaseClass }; const extendedObjects: ExtendedObjects= {} const extendedClasses: ExtendedClasses = { A: ExtendedClassA, B: ExtendedClassB } function initializeBaseObject(key: string): void { extendedObjects[key] = new extendedClasses[key]; } initializeBaseObject("A"); initializeBaseObject("B") extendedObjects["A"].fun(); extendedObjects["B"].fun();
编写上述代码时TypeScript抛出报错:
This expression is not constructible. Type 'BaseClass ' has no construct signatures.
该报错符合预期:BaseClass是抽象类,本身不具备构造签名,无法直接通过new调用。
此前尝试通过any与BaseClass做交叉类型的方式消除报错:
type ExtendedObjects = { [name: string]: any & BaseClass }; type ExtendedClasses = { [name: string]: any & BaseClass };
但该方案引入了不安全的any类型,不符合类型安全要求。
实际业务约束:
- 所有扩展类定义在独立模块中,无法预先知晓
extendedClasses对象中包含的具体类 - 仅能确定所有存入的类均继承自
BaseClass - 需要不使用
any的类型安全声明方案
类型安全解决方案
报错的核心原因是类型定义错配:
ExtendedObjects用于存储实例化后的对象,值类型为BaseClass子类实例,原类型定义正确ExtendedClasses用于存储类本身(即构造函数),原类型定义错误地将值类型写为BaseClass实例,而非可构造的类类型
只需要给ExtendedClasses定义带构造签名的类型即可,完全不需要使用any:
// 存储实例:值为BaseClass子类实例 type ExtendedObjects = { [name: string]: BaseClass }; // 存储类本身:值为无参构造函数,调用后返回BaseClass子类实例 type ExtendedClasses = { [name: string]: new () => BaseClass };
如果扩展类的构造函数需要传入参数,可以根据实际参数类型调整构造签名,例如支持任意参数的写法为new (...args: unknown[]) => BaseClass。
修正后可直接通过TS类型校验,完整代码如下:
abstract class BaseClass { abstract fun(): void; } class ExtendedClassA extends BaseClass { fun(): void { console.log("Class A"); } } class ExtendedClassB extends BaseClass { fun(): void { console.log("Class B"); } } type ExtendedObjects = { [name: string]: BaseClass }; type ExtendedClasses = { [name: string]: new () => BaseClass }; const extendedObjects: ExtendedObjects= {} const extendedClasses: ExtendedClasses = { A: ExtendedClassA, B: ExtendedClassB } function initializeBaseObject(key: string): void { extendedObjects[key] = new extendedClasses[key]; } initializeBaseObject("A"); initializeBaseObject("B") extendedObjects["A"].fun(); extendedObjects["B"].fun();
内容的提问来源于stack exchange,提问作者rgutt
相关产品推荐
相关产品推荐

