如何为TypeScript消息订阅函数的回调动态使用联合类型?
问题:为消息订阅函数的
callback定义类型 我想要实现一个消息订阅函数,无类型的简化版本如下:
function newMessage(message) { postMessage(message) } function subscribe(messageType, callback) { handleNewMessage(message => { if (message.type === messageType) { callback(message.data) } }) }
newMessage的message支持多种类型,因此我通过区分联合类型定义该参数:
type FooMessage = { type: 'foo', data: { a: string, b: number } } type BarMessage = { type: 'bar' } type MessageType = FooMessage | BarMessage
注意BarMessage没有关联的data字段。我已为newMessage完成类型定义:
function newMessage(message: MessageType): void { postMessage(message) }
但不确定如何为subscribe的callback定义类型。目前的代码如下:
function subscribe(messageType: MessageType['type'], callback) { handleNewMessage(message => { if (message.type === messageType) { callback(message.data) } }) }
使用MessageType['data']会报错,因为data并非总是存在;即使给BarMessage定义data:undefined,也会丢失消息类型与data的关联。理想状态下,调用subscribe('bar', (data) => console.log(data))时,TypeScript能识别出data为undefined。我可以为每种消息类型重复定义函数类型,但这样过于繁琐,实际场景中有大量消息类型,希望避免这种方式。请问为subscribe的callback定义类型的最佳方式是什么?
解决方案
可以通过泛型+条件类型实现类型关联,让TypeScript自动根据传入的messageType推导callback的参数类型,无需手动为每种消息类型重复定义:
// 工具类型:根据消息type值,提取对应data类型(无data则返回undefined) type MessageData<T extends MessageType['type']> = Extract<MessageType, { type: T }> extends { data: infer D } ? D : undefined; function subscribe<T extends MessageType['type']>( messageType: T, callback: (data: MessageData<T>) => void ) { handleNewMessage((message: MessageType) => { if (message.type === messageType) { callback('data' in message ? message.data : undefined); } }); }
核心逻辑说明
MessageData工具类型:- 用
Extract<MessageType, { type: T }>从联合类型中精准筛选出type为T的消息类型 - 通过条件类型
infer D自动推断该消息的data字段类型,若不存在data字段则返回undefined
- 用
泛型
subscribe函数:- 泛型
T约束messageType只能是MessageType中已定义的type值 callback的参数类型与messageType强关联,调用时TypeScript会自动推导参数类型,无需手动指定
- 泛型
使用示例
// 传入'foo'时,data自动推导为{ a: string, b: number } subscribe('foo', (data) => { console.log(data.a); // 类型安全,无报错 }); // 传入'bar'时,data自动推导为undefined subscribe('bar', (data) => { console.log(data); // TypeScript会提示data可能为undefined });
这种方案可以适配任意数量的消息类型,既避免了重复定义,又保证了类型的严格校验。
内容的提问来源于stack exchange,提问作者rbhalla
相关产品推荐
相关产品推荐

