You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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);
    }
  });
}

核心逻辑说明

  1. MessageData工具类型:

    • 用Extract<MessageType, { type: T }>从联合类型中精准筛选出type为T的消息类型
    • 通过条件类型infer D自动推断该消息的data字段类型,若不存在data字段则返回undefined
  2. 泛型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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 07:10:27