区分联合类型匹配



我似乎无法匹配函数接受的参数类型和有效负载:

type SelectCase = {
type: 'selectCase';
}
type UpdateNotification = {
type: 'updateNotification';
payload: {
title: string;
message: string;
}
}
type Message = SelectCase | UpdateNotification;
const someFn = <TMessageType extends Message['type']>(
messageType: TMessageType,
onMessage: (
payload?: Extract<Message, { type: TMessageType }> extends {
payload: infer TPayload;
}
? TPayload
: undefined
) => void
) => {
const handleMessage = (message: Message) => {
if(message.type === messageType) {
// does not work
onMessage(message.payload);
}
}
};

这是一个简化的例子,我有超过20种类型的消息。知道为什么这个不行吗?

我已经尝试了类型谓词和调用签名,但它仍然错误。

这里的问题是TypeScript缺乏对相关联合类型的直接支持,如microsoft/TypeScript#30581所述。本质上,类型检查器不容易理解为检查message.type === messageType应该将message缩小到onMessage()可以处理payload属性的Message联合的成员。message的类型在检查后变得messageType的类型相关,而不知道具体是什么。但是没有一种简单的方法可以将这些信息传递给编译器。

通常你可以通过泛型和一些类型重构来解决这个问题,就像microsoft/TypeScript#47109中描述的那样,但我在这里无法让它工作(如果有人让它工作,请告诉我)。


所以,我认为我们能做的最好的事情就是写一个自定义类型保护函数,说message.type === messageType将适当地缩小message。这里有一种方法:

首先,我要将onMessage()参数的条件类型复制到一个我们可以重用的实用程序类型:

type CorrespondingPayload<K extends Message['type']> =
Extract<Message, { type: K }> extends {
payload: infer TPayload;
} ? TPayload : undefined

现在自定义类型保护函数是这样的:

function sameType<K extends Message['type']>(
message: Message, messageType: K
): message is Message & { type: K, payload: CorrespondingPayload<K> } {
return message.type === messageType;
}

如果sameType(message, messageType)返回true,则message将缩小到payload已知为CorrespondingPayload<K>类型的类型。让我们使用它:

const someFn = <K extends Message['type']>(
messageType: K,
onMessage: (payload?: CorrespondingPayload<K>) => void
) => {
const handleMessage = (message: Message) => {
if (sameType(message, messageType)) {
onMessage(message.payload); // okay
}
}
};

看起来不错!

Playground链接到代码

最新更新