6

I'm currently trying to improve the types on some existing code. My code looks roughly like this:

/* 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);
});

I'd like to remove the need to explicitly narrow the type of the message passed to the message handler (where /*REMOVE THIS TYPE HINT!*/ is), such that it correctly narrows to the type that has a matching messageType type (e.g. if messageType is "ADD_COMMENT" then message should be AddCommentMessage).

If this is not possible right now please let me know. I'm under the impression that it isn't but I'm not quite sure.

4

1 回答 1

9

这是不可能的,除非您愿意对代码进行相当多的更改。

您的基本界面

interface Message {
    messageType: string;
}

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

如果您仅将代码限制为及其后代,这里是一个示例,如何通过字符串文字类型(实际上是字符串文字类型的联合)AppMessage制作打字稿来推断您需要的类型: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);
});

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

于 2017-03-20T23:40:17.330 回答