#typescript
#typescript
Вопрос:
Я пытаюсь создать API шины сообщений, где сообщения могут быть отправлены только с полезной нагрузкой определенного типа / формы.
В основном я делаю это для того, чтобы, когда я начинаю вводить вызов emitMessage
(ниже), я хотел бы, чтобы IDE могла предоставлять подсказки типа во время компиляции для payload
аргумента, как только я укажу type
аргумент, чтобы я знал, какой тип payload
ожидается для любого данного типа сообщения.
Я попытался выполнить это с помощью чего-то вроде следующего:
// I create some generic payload interface that can be any shape
interface Payload<T> {
[key: string]: any
}
// I define an enum of permitted message types
enum MessageType {
TEST
}
// And then I expose a function for emitting messages of a particular type
// along with its associated payload
function emitMessage<Msg extends MessageType>(type: Msg, payload: Payload<Msg>) {
// ...
}
Но этот код неполный / неправильный, потому что я понятия не имею, как бы я указал, что определенный вариант MessageType
перечисления связан с определенной специализацией Payload
.
Я не уверен, возможно ли это вообще, поэтому любая помощь в этом направлении была бы высоко оценена.
Комментарии:
1. Итак, вам нужен какой-то маршрутизатор, который в зависимости от типа сообщения отправляет полезную нагрузку какому-либо другому методу, который может с этим справиться?
2. @solarc Нет, возможно, моего объяснения было недостаточно. Когда я вызываю emitMessage, я бы хотел, чтобы IDE могла предоставлять подсказки типа во время компиляции для
payload
после того, как я указалtype
, имеет ли это смысл? Чтобы я знал, какая полезная нагрузка ожидается при предоставлении данного сообщения
Ответ №1:
Я нашел действительно классное решение в виде типов индексов
// I define an enum of permitted message types
enum MessageType {
TEST
}
// This interface is then used as a kind of "type directory"
interface Payload {
[MessageType.TEST]: {
test: boolean
}
}
// payload's type here is now an index into the Payload type directory
function emitMessage<M extends MessageType>(msg: M, payload: Payload[M]) {
switch(msg) {
case MessageType.TEST: {
// payload.test is suggested by my IDE now!
}
}
}
Эта функция потрясающая, и я собираюсь использовать ее для всего
Ответ №2:
Один из подходов заключается в использовании различаемых союзов. Сначала добавьте в свою полезную нагрузку поле, указывающее ее тип, например kind
beliow
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
Сначала мы объявляем интерфейсы, которые будем объединять. Каждый интерфейс имеет свойство kind с другим типом строкового литерала. Свойство kind называется дискриминантом или тегом. Остальные свойства специфичны для каждого интерфейса. Обратите внимание, что интерфейсы в настоящее время не связаны. Давайте объединим их:
type Shape = Square | Rectangle | Circle;
Теперь давайте используем различаемое объединение:
function area(s: Shape) {
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.height * s.width;
case "circle": return Math.PI * s.radius ** 2;
}
}
Смотрите раздел Расширенные типы