typescript 动态使用联合类型定义回调函数

yk9xbfzb  于 2022-12-14  发布在  TypeScript
关注(0)|答案(1)|浏览(165)

我尝试写一个消息订阅函数的等价物。一个没有类型的简化版本看起来像这样:

function newMessage(message) {
  postMessage(message)
}

function subscribe(messageType, callback) {
  handleNewMessage(message => {
    if (message.type === messageType) {
       callback(message.data)
    }
  })
}

newMessagemessage可以是多个类型,因此我分别定义它们,然后使用区分联合来输入message参数。

type FooMessage = {
  type: 'foo', 
  data: {
    a: string,
    b: number
  }
}

type BarMessage = {
  type: 'bar'
}

type MessageType = FooMessage | BarMessage

请注意BarMessage没有任何关联的数据。
这允许我的newMessage函数被键入如下:

function newMessage(message:MessageType):void {
    postMessage(message)
  }

不过我不完全确定如何输入subscribe。这是我目前所拥有的:

function subscribe(messageType: MessageType['type'], callback) {
  handleNewMessage(message => {
    if (message.type === messageType) {
       callback(message.data)
    }
  })
}

我不确定如何输入callback。如果我输入MessageType['data'],我会得到一个错误,因为data并不总是存在。即使我在BarMessage上定义了data:undefined,我也失去了消息类型和数据之间的链接。
理想情况下,我希望能够编写subscribe('bar', (data) => console.log(data))和typescript,以了解data在本例中实际上是未定义的。
我可以看到的一个选项是为我拥有的每个消息类型多次声明一个函数类型,但这感觉过于冗长,因为这意味着对于每个消息类型,我都要定义MessageType和关联的subscribe函数。
在现实中,我有很多很多更多的消息,并不希望这样做。
键入subscribecallback函数的最佳方法是什么?

owfi6suc

owfi6suc1#

您可以使用下列项目:

  • 泛型类型参数T,以便TypeScript可以缩小消息类型(如果它是静态已知的)
  • 具有Extract的分布式条件类型,它允许缩小到分布式联合的特定成员类型,有关详细信息,请参见this SO thread
  • conditional type,仅当data的类型作为关键字存在时,才从消息中提取该类型
type TypedMessage<T extends MessageType['type']> = Extract<MessageType, { type: T }>;

type MessageData<M extends MessageType> = M extends { data: any } ? M['data'] : undefined;

function subscribe<T extends MessageType['type']>(
  messageType: T,
  callback: (data: MessageData<TypedMessage<T>>) => void
) {
  handleNewMessage(message => {
    if (message.type === messageType) {
       callback((message as { data?: any }).data)
    }
  })
}

subscribe('foo', (data) => console.log(data)); // typeof data === FooMessage['data']
subscribe('bar', (data) => console.log(data)); // typeof data === undefined

这里是一个Playground链接TypeScriptPlayground

相关问题