TypeScript模板字面量实现Pub/Sub子事件时泛型约束失效问题
问题原因
你当前的泛型约束失效核心是TypeScript的泛型推导逻辑限制:你声明的subscribeToSubEvent的泛型参数E默认约束为ChatEvent联合类型,当你传入具体事件字符串时,TS不会基于EventWithSubEvent<E>反向收窄E的取值范围,而是直接将E推导为完整的ChatEvent联合("connect" | "disconnect"),因此出现两个问题:
- 回调payload被推导为所有事件payload的联合类型
- 不会校验子事件和父事件的匹配关系,非法组合也能通过校验
解决方案
最简洁且性能最优的方案是先预构造全量合法事件与payload的映射表,直接基于映射表做类型约束:
// 构造全量合法事件映射 type FullEventMap = { [E in ChatEvent as EventWithSubEvent<E>]: ChatEvents[E] } // 重写订阅函数,直接约束事件为映射表的key declare function subscribeToSubEvent<E extends keyof FullEventMap>( event: E, callback: (payload: FullEventMap[E]) => void ): void;
修改后即可解决所有问题:
- 传入合法事件
"connect:general"时,payload会正确推导为{ user: string } - 传入非法事件
"connect:voice"时,会直接触发类型错误,提示该事件不在合法范围内
你还可以直接合并为一个统一的订阅函数,覆盖根事件和带子事件的所有场景,符合你最开始期望的调用语法:
declare function on<E extends keyof FullEventMap>( event: E, callback: (payload: FullEventMap[E]) => void ): void; // 两种调用都正常触发类型校验 on("connect", payload => { /* payload为connect事件类型 */ }) on("connect:general", payload => { /* payload同样为connect事件类型 */ }) on("disconnect:voice", payload => { /* payload为disconnect事件类型 */ })
内容的提问来源于stack exchange,提问作者Slava Knyazev
相关产品推荐
相关产品推荐

