TypeScript如何通过映射类型窄化常量映射表的实例类类型
问题原因
- 方法入参
key的类型被写死为WidgetType全量联合类型,没有和泛型参数T绑定,TS无法建立入参和返回值的关联 - TS默认不会对常量对象的动态索引访问做跨泛型的类型窄化,动态取构造函数实例化时,会默认推导为所有可能实例的联合类型
修复实现
只需要做两处调整即可实现精准类型推导:
- 将
getHandler的入参类型从WidgetType改为T,让入参和泛型强绑定 - 对映射表取出的构造函数做对应泛型的类型断言(逻辑上保证枚举值和映射表一一对应,类型安全)
修复后完整可运行代码:
export enum WidgetType { PIE = "pie", BAR = "bar", COUNTER = "counter", } class CounterHandler{} class GroupValuesHandler{} const HANDLERS_MAP = { [WidgetType.COUNTER]: CounterHandler, [WidgetType.BAR]: GroupValuesHandler, [WidgetType.PIE]: GroupValuesHandler, } as const; export type GetHandler<T extends WidgetType> = { [K in WidgetType]: typeof HANDLERS_MAP[K]; }[T]; // 验证单独类型推导正常 type B = GetHandler<WidgetType.COUNTER>; // CounterHandler export class WidgetHandlerMapper { public getHandler<T extends WidgetType>( key: T, ): InstanceType<GetHandler<T>> { const HandlerClass = HANDLERS_MAP[key] as GetHandler<T>; return new HandlerClass(); } } const mapper = new WidgetHandlerMapper() const counterHandler = mapper.getHandler(WidgetType.COUNTER); // 类型为CounterHandler,符合预期 const barHandler = mapper.getHandler(WidgetType.BAR); // 类型为GroupValuesHandler,符合预期 const pieHandler = mapper.getHandler(WidgetType.PIE); // 类型为GroupValuesHandler,符合预期
补充说明
- 该实现要求
HANDLERS_MAP必须覆盖WidgetType的所有枚举值,如果存在遗漏会出现运行时错误,可按需增加运行时判空兜底逻辑 - 如果需要更强的类型校验,也可以通过方法重载逐个声明每个枚举值对应的返回类型,但是泛型方案代码更简洁,后续新增枚举值时不需要同步修改方法签名,维护成本更低
内容的提问来源于stack exchange,提问作者nook
相关产品推荐
相关产品推荐

