TS2339: Свойство не распознается в переменном типе

type NotifyOptions = {
    className: string
    timeout?: null | number
} & ({ text: string } | { html: string })


export default function notify(options: NotifyOptions) {
  if (options.text) {
    console.info(options.text)
  }
}

Property 'text' does not exist on type 'NotifyOptions'. Property 'text' does not exist on type '{ className: string; timeout?: number | null | undefined; } & { html: string; }'.(2339)

детская площадка

Почему говорится, что options.text не существует? Я думаю, он должен быть набран как string|undefined.

Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой 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 для повышения производительности приложения путем загрузки модулей только тогда, когда они...
0
0
31
2

Ответы 2

Эта площадка показывает рабочий пример, который допускает «размеченное объединение». Это работает так: он выделяет определенный тип с помощью проверки, которую Typescript действительно может использовать (буквальное равенство). См. дискриминируемые союзы.

type NotifyOptions = {
  className: string;
  timeout?: null | number;
} & ({
  kind: "html";
  html: string;
}|{
  kind: "text";
  text: string;
});

export default function notify(options: NotifyOptions) {
  if (options.kind === "text") {
    console.info(options.text);
  }
  else if (options.kind === "html") {
    console.info(options.html);
  }
}

К сожалению, как работает машинописный текст, вы можете ссылаться только на те свойства, которые существуют во всех случаях объединения. Следовательно, необходимо иметь свойство kind. Вы можете увидеть, когда jcalz научил меня этому на https://stackoverflow.com/a/65495776/2257198

Если в вашем случае действительно можно позволить себе не обращать внимания на систему типов (например, вас интересует только наличие свойства и возможность разыменовать его независимо от его типа), тогда наименьшее изменение исходного кода - использование Оператор in, который устраняет подобную ошибку компилятора.

export default function notify(options: NotifyOptions) {
  if ("text" in options) {
    console.info(options.text)
  }
}

Вы можете пойти еще дальше по пути ввода javascript, опять же, если вам не нужны типы Typescript для вашего случая ...

export default function notify(options: NotifyOptions) {
  if ("text" in options && typeof options.text === "string") {
    console.info(options.text)
  }
}

Машинопись позволит вам получить доступ только к свойствам, общим для каждого члена союза.

С другой стороны, это не позволит вам получить доступ к свойствам, определенным только в одном члене объединения.

Предположение о том, что элементы, существующие в одном члене объединения, не существуют в других элементах, на практике может не соответствовать действительности: они могут существовать, и тип свойства может быть другим. Таким образом, предположение, что options.text является строкой или неопределенным, считается небезопасным.

Рассмотрим следующий пример:

const o = {
    className: 'c1',
    timeout: 3000,
    html: 'ht',
    text: 4
};
const opt: NotifyOptions = o;

Я создал NotifyOptions, где текст - это число.

В Typescript 3.2 появился Неединичные типы как дискриминанты объединения

TypeScript 3.2 makes narrowing easier by relaxing rules for what it considers a discriminant property. Common properties of unions are now considered discriminants as long as they contain some singleton type (e.g. a string literal, null, or undefined), and they contain no generics.

Ты можешь написать:


type NotifyOptions = {
    className: string
    timeout?: null | number
} & ({ text: string; html?: undefined; } 
   | { html: string; text?: undefined})

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

Кроме того, назначение константы o для NotifyOptions больше не разрешено.

Ссылка на игровую площадку

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