Typescript делает вывод, что поле является истинным (не может быть неопределенным), без определяемых пользователем средств защиты типов

type Item = {
    left?: { photoSrc: string };
};

type ItemRequired = {
    left: { photoSrc: string };
};

const item: Item = {} as any;

if (item.left) {
    const itemRequired: ItemRequired = item; // fails. Typescript still says left may be undefined
    // I'd like Typescript to know that field left is truthy
}

Я знаю об охранниках определяемого пользователем типа. Например.

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

Мои вопросы:

  1. Почему Typescript не может сделать вывод, что left не может быть неопределенным?
  2. Означает ли это, что мне нужно создать функцию защиты типа для каждого варианта типа, который мне нужно проверить? Нет ли способа попроще? Например. Я ожидаю, что если вы попадете в область действия проверки if, которая доказывает, что поле left не может быть неопределенным, я ожидаю, что Typescript это знает.

Это кажется очень частым случаем, но я не смог найти ответа, извините, пожалуйста, если он есть.


person ZenVentzi    schedule 31.05.2020    source источник


Ответы (2)


Чтобы ответить на ваши вопросы:

Я предполагаю, потому что Typescript здесь сравнивает два типа: Item и ItemRequired, а не то, что вам нужно, а именно { left:{ photoSrc:string } }.

TypeScript часто может определить, какая часть объединения типов используется без защиты типов, но это еще не совсем идеально. Все зависит от того, что будет работать, а что нет, существует множество возможных ситуаций.

Часто люди хотят обойтись без защиты типов, и это понятно, поскольку часто это кажется дополнительным, ненужным кодом. Но он действительно разработан для этой конкретной ситуации:

const itemIsItemRequired = (item: Item): item is ItemRequired =>
  item.left !== undefined;

if (itemIsItemRequired(item)) {
    const itemRequired: ItemRequired = item; // this works
}
person OliverRadini    schedule 31.05.2020

В настоящий момент Typescript не выводит типы из условного потока операторов.

const item: Item = {} as any;
if (item.left) {
    const itemRequired: ItemRequired = item; 
}

В результате приведенный выше блок кода фактически не сужает тип элемента с Item до ItemRequired. Следовательно, компилятору вы пытаетесь назначить два несовместимых типа: один, где left, является обязательным, а другой, где это необязательно.

В качестве альтернативы, как вы упомянули, вы можете написать охранник типа или просто сделать следующее.

const itemRequired: ItemRequired = item as ItemRequired;

Тем не менее, всегда рекомендуется использовать защитные ограждения письменного типа.

person Nafiz Ahmed    schedule 31.05.2020