如何在TypeScript中存储类型并实现动态实例化?
TypeScript实现类似C#的反射动态实例化类
问题描述
想要在TypeScript中实现类似C#通过反射基于Type类动态实例化类的功能,以下是C#的实现示例:
interface IHandler { void Handle(); } var handlers = new Dictionary<string, Type> { { "querydom", typeof(QueryDomHandler) }, { "other", typeof(OtherHandler) }, }; var type = handlers["querydom"]; var instance = (IHandler)Activator.CreateInstance(type, args); instance.Handle();
尝试编写TypeScript代码草稿时,遇到两个问题:不知道如何存储类的“类型引用”,也不清楚如何不直接使用类名动态实例化类:
let handlers = []; handlers[CommandType.QueryDom] = QueryDomCommandHandler; // 如何存储"类型"? chrome.runtime.onMessage.addListener((message: Command, sender, sendResponse) => { logger.debug(`${isFrame ? 'Frame' : 'Window'} '${document.location.href}' received message of type '${CommandType[message.command]}'`); const handlerType = handlers[CommandType.QueryDom]; const handlerInstance = ????? // 如何实例化类? if (message.command == CommandType.QueryDom) { const handler = new QueryDomCommandHandler(message as RulesCommand); const response = handler.handle(); sendResponse(response); return true; } else if (message.command == CommandType.Highlight) { const handler = new HighlightCommandHandler(message as RulesCommand); handler.handle(); } });
更新:已实现基础方案,但希望用Enum替代Record中的硬编码字符串,尚未找到方法:
const handlers: Record<string, (new () => commands.CommandHandlerBase)> = { 'QueryDom': QueryDomCommandHandler, 'Highlight': HighlightCommandHandler, 'ClearHighlight': ClearHighlightCommandHandler, }; const handlerType = handlers[commands.CommandType[message.command]]; const handler = new handlerType(); const response = await handler.handle(message);
解决方案
1. 基础实现:存储类引用并动态实例化
在TypeScript中,类本身就是可直接存储的构造函数引用,无需像C#那样通过typeof获取类型。可通过Record类型定义存储结构,明确构造函数的类型约束:
步骤1:统一基类与构造函数签名
确保所有处理器类继承自同一基类(如CommandHandlerBase),并保持构造函数签名一致:
// 基类定义 abstract class CommandHandlerBase { abstract handle(message: Command): Promise<any> | void; } // 示例处理器类 class QueryDomCommandHandler extends CommandHandlerBase { constructor(private message: RulesCommand) { super(); } async handle() { // 业务逻辑实现 } }
步骤2:定义处理器映射
用Record构建枚举与处理器类的映射,键为CommandType枚举值,值为对应类的构造函数:
enum CommandType { QueryDom, Highlight, ClearHighlight } const handlers: Record<CommandType, new (message: RulesCommand) => CommandHandlerBase> = { [CommandType.QueryDom]: QueryDomCommandHandler, [CommandType.Highlight]: HighlightCommandHandler, [CommandType.ClearHighlight]: ClearHighlightCommandHandler };
步骤3:动态实例化与调用
在消息监听逻辑中,通过枚举值获取构造函数,实例化后调用处理方法:
chrome.runtime.onMessage.addListener(async (message: Command, sender, sendResponse) => { logger.debug(`${isFrame ? 'Frame' : 'Window'} '${document.location.href}' received message of type '${CommandType[message.command]}'`); const handlerType = handlers[message.command]; // 处理无对应处理器的边界情况 if (!handlerType) { logger.error(`No handler registered for command: ${CommandType[message.command]}`); return; } const handler = new handlerType(message as RulesCommand); const response = await handler.handle(); sendResponse(response); return true; });
2. 进阶:用Enum替代硬编码字符串
若CommandType定义为字符串值枚举,可直接将其作为Record的键类型,彻底消除硬编码字符串:
步骤1:定义字符串值Enum
enum CommandType { QueryDom = 'QueryDom', Highlight = 'Highlight', ClearHighlight = 'ClearHighlight' }
步骤2:更新处理器映射
直接使用Enum成员作为键,无需硬编码字符串:
const handlers: Record<CommandType, new () => commands.CommandHandlerBase> = { [CommandType.QueryDom]: QueryDomCommandHandler, [CommandType.Highlight]: HighlightCommandHandler, [CommandType.ClearHighlight]: ClearHighlightCommandHandler };
步骤3:简化获取逻辑
由于message.command本身为CommandType类型,直接用其作为键即可:
const handlerType = handlers[message.command]; // 后续实例化与调用逻辑同上
这样既保证类型安全,又完全替代硬编码字符串,满足需求。
内容的提问来源于stack exchange,提问作者JobaDiniz
相关产品推荐
相关产品推荐

