Определите тип свойства на основе другого свойства в том же интерфейсе - PullRequest
1 голос
/ 30 июня 2019

У меня довольно простой интерфейс

type MessageType = 'name' | 'age';
type MessageData = string | number 

interface Message {
    type: MessageType;
    data: MessageData;
}

То, чего я сейчас пытаюсь достичь, это при создании объекта, который реализует Message с типом name, чтобы дать знать TypeScript,что data должен иметь тип string и, если это не так, использовать его.

const message: Message = {
    type: 'name',
    data: 'Michael'
}

Я вроде достиг этого с помощью этой реализации

type MessageType = 'name' | 'age';
type MessageData<T extends MessageType> = T extends 'name' ? string : number;

interface Message<T extends MessageType> {
    type: T;
    data: MessageData<T>;
}

const message: Message<'name'> = {
    type: 'name',
    data: 'Michael'
}

Однако,Я не хочу предоставлять MessageType, а вместо этого Message использовать значение, которое я передаю для type.

С этим сценарием можно поиграть здесь

SOULTION

Масштабируемое решение, найденное с помощью jcalz :

interface MessageDataMap {
    name: string;
    age: number;
    gender: 'male' | 'female';
    // ....
}

type MessageType = keyof MessageDataMap;

type Message = {
  [K in MessageType]: {
    type: K;
    data: MessageDataMap[K];
  };
}[MessageType];

1 Ответ

2 голосов
/ 30 июня 2019

Я бы использовал псевдоним типа вместо интерфейса для 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 с относительной легкостью.

Ссылка на код

Хорошо, надеюсь, это поможет; удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...