TypeScript中如何让subscribe回调仅接收联合类型对应事件
TypeScript订阅函数类型不兼容问题解决
问题代码
以下是用于订阅特定事件类型的TypeScript代码,基于联合类型提取事件:
type Notification = { readonly type: "new_photo"; } | { readonly type: "liked_photo"; readonly id: string } | { readonly type: "disliked_photo"; readonly id: string } type EventType = "new_photo" | "liked_photo" | "disliked_photo" type Subscribers = { [K in EventType]?: Set< (message: Extract<Notification, { type: K }>) => void >; }; const subscribers: Subscribers = {}; export function subscribe<T extends EventType>( type: T, callback: (message: Extract<Notification, { type: T }>) => void, ) { if (!subscribers[type]) { subscribers[type] = new Set(); // 此处出现类型错误 } subscribers[type]?.add(callback); return () => { subscribers[type]?.delete(callback); }; }
初始化时的类型错误
上述代码中,初始化Set时出现以下类型错误:
Type 'Set<(message: Extract<{ readonly type: "new_photo"; }, { type: T; }> | Extract<{ readonly type: "liked_photo"; readonly id: string & Brand<"PhotoId">; }, { type: T; }> | ... 5 more ... | Extract<...>) => void>' is not assignable to type 'Subscribers[T]'.
Type 'Set<...>' is not assignable to type 'Set<(message: { readonly type: "new_photo"; }) => void> & Set<...>'.
...(省略后续错误详情)
临时修复后的新问题
尝试用new Set([])消除初始化错误后,调用回调时又出现新错误,提示参数应为never类型:
export function subscribe<T extends EventType>( type: T, callback: (message: Extract<Notification, { type: T }>) => void, ) { if (!subscribers[type]) { subscribers[type] = new Set([]); // 此处无错误 } subscribers[type]?.add(callback); return () => { subscribers[type]?.delete(callback); }; } function callSubscribers(event: Notification) { if (subscribers[event.type]) { subscribers[event.type]?.forEach((cb) => { cb(event); // 错误:参数应为never类型 }); } }
解决方案
1. 修复初始化时的类型错误
在初始化Set时,使用类型断言明确告知TypeScript该集合的类型为对应事件的回调集合:
export function subscribe<T extends EventType>( type: T, callback: (message: Extract<Notification, { type: T }>) => void, ) { if (!subscribers[type]) { subscribers[type] = new Set() as Subscribers[T]; // 添加类型断言 } subscribers[type]?.add(callback); return () => { subscribers[type]?.delete(callback); }; }
2. 修复回调调用时的类型错误
有两种常用方式解决此问题:
方式一:泛型收窄事件类型
重构callSubscribers函数,用泛型明确事件与回调的匹配关系:
function callSubscribers<K extends EventType>(event: Extract<Notification, { type: K }>) { const eventSubscribers = subscribers[event.type] as Subscribers[K]; eventSubscribers?.forEach(cb => cb(event)); }
方式二:显式分支判断
通过switch分支让TypeScript自动收窄类型:
function callSubscribers(event: Notification) { switch (event.type) { case "new_photo": subscribers.new_photo?.forEach(cb => cb(event)); break; case "liked_photo": subscribers.liked_photo?.forEach(cb => cb(event)); break; case "disliked_photo": subscribers.disliked_photo?.forEach(cb => cb(event)); break; } }
原理说明
- 初始化错误:TypeScript无法自动将泛型
T对应的具体回调集合类型与new Set()的推断类型关联,类型断言直接明确了集合的正确类型。 - 调用回调错误:
subscribers[event.type]的类型是所有事件回调集合的联合,TypeScript无法自动推断当前event与回调参数的匹配关系,通过泛型收窄或显式分支可解决此问题。
内容的提问来源于stack exchange,提问作者Danielo515
相关产品推荐
相关产品推荐

