Я бы использовал псевдоним типа вместо интерфейса для Message
, поскольку вы, естественно, говорите о распознаваемом объединении , а интерфейсы не могут представлять объединения. Я бы определил это так:
type Message = {
[K in MessageType]: { type: K; data: MessageData<K> }
}[MessageType];
Первая часть - это сопоставленный тип , который создает объект типа {name: {type: "name", data: string}, age: {type: "age", data: number}
, а последняя часть - поисковый тип , который получает объединение значений этого типа. .. а именно {type: "name", data: string} | {type: "age", data: number}
. Затем это должно работать так, как вы ожидаете:
const desiredMessage: Message = {
type: "name",
data: "Michael"
};
const alsoDesiredMessage: Message = {
type: "age",
data: 14
};
const badMessage: Message = {
type: "name",
data: 14
}; // error! number is not string
Ссылка на код
Обратите внимание, что ваши типы MessageType
и MessageData
представляют ваше ограничение несколько косвенно ... условный тип для MessageData
, вероятно, плохо масштабируется, если MessageType
начинает расти. Вместо этого я бы предложил использовать вспомогательный интерфейс, подобный этому:
interface MessageDataMap {
name: string;
age: number;
}
Тогда ваши MessageType
и MessageData
могут быть представлены как keyof
/ типы поиска
type MessageType = keyof MessageDataMap
type MessageData<T extends MessageType> = MessageDataMap[T]
Это будет вести себя точно так же, как и раньше, но вы можете добавить свойства к MessageDataMap
с относительной легкостью.
Ссылка на код
Хорошо, надеюсь, это поможет; удачи!