Вывод типа 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);
});
Я хотел бы убрать необходимость явно сузить тип сообщения, передаваемого обработчику сообщений (где /*REMOVE THIS TYPE HINT!*/), таким образом, чтобы он правильно сужался до типа, который имеет соответствующий тип messageType (например, если messageType является "ADD_COMMENT", то message должен быть AddCommentMessage).
1 ответ:
Это невозможно, если только вы не хотите немного изменить код.
Ваш базовый интерфейс
interface Message { messageType: string; }Слишком общий, Я думаю, что
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); });Я также удалил свойство
messageTypeИз интерфейсов, чтобы избежать дублирования, я думаю, что тип пересечения в параметреhandlerдостигает того же эффекта.