Типы Союза - Разрешить разные подтипы с | - PullRequest
2 голосов
/ 13 июля 2020

Я набираю имя Entry, то есть:

export type Entry = {
  number: number
  position: number
  entryItem: Banana | Orange
}

И затем я определяю типы entryItem как:

Тип банана

export type Banana = {
  number: number
  bananaTitle: string
}

Тип Apple

export type Apple = {
  number: number
  appleTitle: string
}

Теперь, если я сделаю что-то вроде:

entry.entryItem.bananaTitle

, я получаю сообщение об ошибке: «Свойство 'bananaTitle' делает не существует для типа 'Banana | Apple' "

Аналогично, если я это сделаю:

entry.entryItem.appleTitle

Я получаю сообщение об ошибке:« Свойство 'appleTitle' не существует для типа 'Banana | Apple '"

Насколько я понимаю, оператор | позволит мне создать тип, который" умно "поймет, имею ли я в виду Banana или Apple, когда я делаю что-то вроде entry.entryItem.UNIQUE_PROPERTY_OF_BANANA_OR_APPLE

Что мне не хватает?

Ответы [ 2 ]

2 голосов
/ 13 июля 2020

Составитель не волшебник. Свойство entryItem может быть Banana или Apple. Поскольку мы находимся на уровне компиляции, вы должны сообщить компилятору, что такое текущий объект. Есть разные способы. Один из них - использовать оператор in, например

function test(entry: Entry) {
  if ("appleTitle" in entry.entryItem) {
     entry.entryItem.appleTitle; // it's an apple
  } else {
    entry.entryItem.bananaTitle; // it's a banana
  }
}
1 голос
/ 13 июля 2020

Нужно принять во внимание две точки зрения. Код, который создает запись, и код, который использует запись. Этот тип означает, что если вы создаете Entry, то entryItem может быть бананом или яблоком. У вас больше возможностей, чем было бы в противном случае.

Но что касается кода, который использует Entry, он содержит меньше информации, чем было бы в противном случае. Вы можете иметь дело с яблоком или бананом. Невозможно определить автоматически, поэтому по умолчанию вы можете получить доступ только к свойствам, которые гарантированно присутствуют. Ie, свойства найдены для обоих типов.

Если вы хотите написать код, который взаимодействует конкретно с яблоками или, в частности, с бананами, вам сначала нужно написать код, который выясняет, с каким из них вы имеете дело .

Один из способов сделать это - использовать оператор in, чтобы узнать, присутствует ли свойство:

const Example = (entry: Entry) => {
  if ('appleTitle' in entry.entryItem) {
    // Inside this block, typescript knows that item must be an Apple, so you can access things found only on apples.
    console.log(entry.entryItem.appleTitle);
  } else {
    console.log(entry.entryItem.bananaTitle);
  }
}

Другой вариант - изменить ваши типы на « размеченное объединение". Другими словами, у обоих типов есть свойство, которое может однозначно идентифицировать то, с чем мы имеем дело. Например:

export type Banana = {
  type: 'banana',
  number: number
  bananaTitle: string
}

export type Apple = {
  type: 'apple',
  number: number
  appleTitle: string
}

// then use it like:
const Example = (entry: Entry) => {
  if (entry.entryItem.type === 'apple') {
    // Inside this block, typescript knows that item must be an Apple, so you can access things found only on apples.
    console.log(entry.entryItem.appleTitle);
  } else {
    console.log(entry.entryItem.bananaTitle);
  }
}

Третий вариант - создать охранник определяемого пользователем типа

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