TypeScript类型推断/缩小挑战

Pat*_*Pat 5 inference type-inference narrowing typescript

我目前正在尝试改进现有代码的类型.我的代码看起来大致如下:

/* dispatcher.ts */
interface Message {
    messageType: string;
}

class Dispatcher<M extends Message> {    
    on<
        MessageType extends M["messageType"],
        SubMessage extends M & { messageType: MessageType }
    >(
        messageType: MessageType,
        handler: (message: SubMessage) => void
    ): void { }
}

/* messages.ts */
interface AddCommentMessage {
    messageType: "ADD_COMMENT";
    commentId: number;
    comment: string;
    userId: number;
}

interface PostPictureMessage {
    messageType: "POST_PICTURE";
    pictureId: number;
    userId: number;
}

type AppMessage = AddCommentMessage | PostPictureMessage;

/* app.ts */
const dispatcher = new Dispatcher<AppMessage>();

dispatcher.on("ADD_COMMENT", (message: AddCommentMessage ) => {
                                    /* ^^ REMOVE THIS TYPE HINT!*/
    console.log(message.comment);
});
Run Code Online (Sandbox Code Playgroud)

我想删除需要明确地缩小传递给消息处理器(其中消息类型/*REMOVE THIS TYPE HINT!*/是),使得它正确地变窄到具有匹配的类型messageType的类型(例如,如果messageType"ADD_COMMENT"message应该是AddCommentMessage).

如果现在不可能,请告诉我.我的印象是它不是,但我不太确定.

art*_*tem 6

这是不可能的,除非你愿意更改代码.

你的基础界面

interface Message {
    messageType: string;
}
Run Code Online (Sandbox Code Playgroud)

太笼统了,我认为messageType: string排除了基于该值的任何推断messageType,并且看起来不可能在Dispatcher界面中充分缩小它.

如果AppMessage仅将代码限制为及其后代,下面是一个示例,如何使用typescript来推断所需的类型,由字符串文字类型引导(keyof AppMessageMap实际上是字符串文字类型的联合"ADD_COMMENT" | "POST_PICTURE"):

/* dispatcher.ts */

class Dispatcher {    
    on<
        MessageType extends keyof AppMessageMap
    >(
        messageType: MessageType,
        handler: (message: AppMessageMap[MessageType] & {messageType: MessageType}) => void
    ): void { }
}

/* messages.ts */
interface AddCommentMessage {
    commentId: number;
    comment: string;
    userId: number;
}

interface PostPictureMessage {
    pictureId: number;
    userId: number;
}

interface AppMessageMap {
    "ADD_COMMENT": AddCommentMessage,
    "POST_PICTURE": PostPictureMessage
}
type AppMessage = AppMessageMap[keyof AppMessageMap];

/* app.ts */
const dispatcher = new Dispatcher();


dispatcher.on("ADD_COMMENT", (message) => {
    console.log(message.comment);
});
Run Code Online (Sandbox Code Playgroud)

我还messageType从接口中删除了属性以避免重复,我认为handler参数中的交集类型实现了相同的效果.

  • 这是完美的答案,非常感谢你.它总是让我觉得TypeScript的类型系统有多强大.我唯一改变的是将`Dispatcher`设为通用,以便将'AppMessageMap`作为类型参数传递(我在同一代码库中对多个应用程序使用相同的`Dispatcher`). (2认同)