TypeScript выводит тип Never, когда функция предиката типа возвращает false

Я использую функцию, которая возвращает предикат типа, чтобы сообщить TypeScript, что источник определенно имеет или не имеет свойства media. Если это SourceWithoutMedia, его следует обрабатывать одним способом, а если это SourceWithMedia, его следует обрабатывать другим способом.

Проблема в том, что TypeScript делает из случая, когда предикат типа возвращает false.

Я бы ожидал, что TypeScript сделает вывод, что если isSourceWithoutMedia(source) возвращает false, то source является SourceWithMedia.

На самом деле он подразумевает, что если isSourceWithoutMedia(source) возвращает false, то тип sourcenever.

Чего мне здесь не хватает, и есть ли способ заставить TypeScript сделать вывод, что если isSourceWithoutMedia(source) возвращает false, то source является SourceWithMedia?

interface SourceWithMedia {
    id: string
    someOtherProperty: string
    media?: string
}

interface SourceWithoutMedia {
    id: string
}

const isSourceWithoutMedia = (source: SourceWithMedia | SourceWithoutMedia): source is SourceWithoutMedia => {
    return Object.keys(source).length === 1
}

function doSomethingWithMedia (source: SourceWithMedia | SourceWithoutMedia) {
    if (isSourceWithoutMedia(source)) return source
    
    if (source.media) return source.media // Error: Property 'media' does not exist on type 'never'.
    
}

Пример на игровой площадке TypeScript

Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
2
0
60
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

TypeScript не понимает взаимосвязь между первым условным блоком и вторым.

Решением этой проблемы было бы явное приведение источника к SourceWithMedia.

function doSomethingWithMedia (source: SourceWithMedia | SourceWithoutMedia) {
  if (isSourceWithoutMedia(source)) return source

  const sourceWithMedia = source as SourceWithMedia
  if (sourceWithMedia.media) return sourceWithMedia.media;
}

TypeScript, возможно, и не идеален, но это определенно не языковая проблема.

Behemoth 16.04.2024 21:32
Ответ принят как подходящий

... если isSourceWithoutMedia(source) возвращает false, то source является ИсточникСМедиа?

Нет.

Проблема в том, что SourceWithMedia расширяет SourceWithoutMedia i. е. SourceWithMedia — это подтип SourceWithoutMedia.

const withMedia: SourceWithMedia = {id: "", someOtherProperty: "", media: ""}
const withoutMedia = withMedia; // valid

При попытке сузить source до SourceWithoutMedia с использованием этого предиката на самом деле ничего не происходит. source по-прежнему будет иметь тип объединения, даже если вы используете что-то вроде !("media" in source) в своем предикате или сначала проверяете отсутствие отсутствующего параметра media, например:

function doSomethingWithMedia(source: SourceWithMedia | SourceWithoutMedia) {
  if (isSourceWithoutMedia(source)) {
    source;
    //^? (parameter) source: SourceWithMedia | SourceWithoutMedia
  }
}

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


С другой стороны, если вы инвертируете логику предикатов и проверите, присутствует ли свойство media явно, вы сможете сузить source до SourceWithMedia. Таким образом, TypeScript действительно может различать эти два типа и сообщать, что мы имеем дело со значением типа SourceWithMedia, потому что SourceWithoutMedia нельзя присвоить SourceWithMedia.

Игровая площадка TypeScript

Другие вопросы по теме