TypeScript中如何声明属性为抽象类的非抽象子类类型?
问题:解决TypeScript中抽象类实例化的类型报错
我正在编写代码,需要从配置文件中加载指定的模块列表。所有模块类在启动时加载,随后根据该列表初始化所需模块。
模块基类代码
export abstract class Module { abstract initialize(config: any): void; abstract shutdown(): void; public static getModuleName(): string { throw new Error('Module must define a moduleName'); } }
模块注册示例
import {Module} from "../../framework/Module"; import {moduleRegistry} from "../../framework/ModuleRegistry"; class TwitchInitializer extends Module { public static MODULE_NAME: string = "twitch"; public static getModuleName(): string { return TwitchInitializer.MODULE_NAME; } initialize(config: any): void { } shutdown(): void { } } moduleRegistry.register(TwitchInitializer);
模块注册表代码
import {Module} from "./Module"; class ModuleRegistry { modules: Record<string, typeof Module> = {}; register<T extends typeof Module>(module: T): void { const moduleName = module.getModuleName(); if (this.modules.hasOwnProperty(moduleName)) { throw `Module ${moduleName} already registered`; } this.modules[moduleName] = module; } get(moduleName: string): typeof Module { return this.modules[moduleName]; } } const moduleRegistry = new ModuleRegistry(); export {moduleRegistry};
模块初始化代码
const modules = []; // modulesToLoad 是字符串数组,例如 ['twitch'] const modulesToLoad = configManager.get('MODULES').split(','); modulesToLoad.forEach((moduleName) => { const moduleType = moduleRegistry.get(moduleName); if (moduleType === undefined) { logger.warn(`Could not load module of type ${moduleName}`); return; } modules.push(new moduleType); })
报错信息
TS2511: Cannot create an instance of an abstract class.
报错位置在new moduleType行,需要正确声明modules数组,使其存储Module抽象类的非抽象子类实例,解决该报错。
解决方案
问题根源在于ModuleRegistry的类型声明:当前modules属性的类型Record<string, typeof Module>指向抽象类的构造函数,TypeScript会阻止直接实例化抽象类。我们需要调整类型约束,确保注册表只处理可实例化的子类构造函数,并明确数组类型。
1. 定义可实例化的模块构造函数类型
在Module所在文件中,添加类型别名,限定仅接受非抽象子类的构造函数:
export abstract class Module { abstract initialize(config: any): void; abstract shutdown(): void; public static getModuleName(): string { throw new Error('Module must define a moduleName'); } } // 声明可实例化的Module子类构造函数类型 export type ModuleConstructor = new () => Module;
2. 更新ModuleRegistry的类型约束
替换注册表中typeof Module为ModuleConstructor,确保存储的都是可实例化的类:
import {Module, ModuleConstructor} from "./Module"; class ModuleRegistry { modules: Record<string, ModuleConstructor> = {}; register(module: ModuleConstructor): void { const moduleName = module.getModuleName(); if (this.modules.hasOwnProperty(moduleName)) { throw `Module ${moduleName} already registered`; } this.modules[moduleName] = module; } get(moduleName: string): ModuleConstructor | undefined { return this.modules[moduleName]; } } const moduleRegistry = new ModuleRegistry(); export {moduleRegistry};
3. 明确modules数组的类型
在初始化代码中,直接指定数组类型为Module[],清晰表明存储的是模块实例:
// 明确声明数组存储Module实例(所有非抽象子类实例都符合该类型) const modules: Module[] = []; const modulesToLoad = configManager.get('MODULES').split(','); modulesToLoad.forEach((moduleName) => { const moduleType = moduleRegistry.get(moduleName); if (moduleType === undefined) { logger.warn(`Could not load module of type ${moduleName}`); return; } // TypeScript现在识别moduleType为可实例化构造函数,不再报错 modules.push(new moduleType()); })
额外优化:增强配置类型安全
如果需要为不同模块定义专属配置类型,可以让Module支持泛型:
export abstract class Module<TConfig = any> { abstract initialize(config: TConfig): void; abstract shutdown(): void; public static getModuleName(): string { throw new Error('Module must define a moduleName'); } } export type ModuleConstructor<TConfig = any> = new () => Module<TConfig>;
这样注册模块时可以指定具体配置类型,进一步提升代码的类型安全性。
内容的提问来源于stack exchange,提问作者Navarr
相关产品推荐
相关产品推荐

