Интерфейс Typescript: тип поля зависит от другого значения поля, используя пересечение интерфейса '&'

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

См. код ниже:

interface IActivityNote {
  type: "note";
  data: {
    title: string;
    content?: string;
  };
}
interface IActivityArchive {
  type: "archive";
  data: { reason: string };
}
type TActivityData = IActivityNote | IActivityArchive;

interface IActivityBase {
  id: string;
  ownerId: string;
}
type TActivity = IActivityBase & TActivityData;

interface IActivityServerBase {
  id: string;
  owner: string;
}
type TActivityServer = IActivityServerBase & TActivityData;

const mapToActivityServer = (activity: TActivity): TActivityServer => ({
  id: activity.id,
  owner: activity.ownerId,
  type: activity.type,
  data: activity.data
});

Почему TS не получает его сам по себе? Должен ли я помещать условие внутри функции, чтобы указать структуру данных на основе значения типа?

Воспроизведение здесь: https://codesandbox.io/s/typescript-infering-type-value-with-interface-intersection-svdgsd?file=/src/index.ts

В основном я хочу, чтобы тип был либо:

{
  id: string
  owner: string
  type: 'note'
  data: { title: string; content?: string }
}

// OR

{
  id: string
  owner: string
  type: 'archive'
  data: { reason: string }
}

Но используя тип интеллектуального пересечения, чтобы избежать дублирования всех типов объектов для каждого нового значения «типа» с другой структурой «данных», которую я хочу реализовать.

В настоящее время вы делаете союз, а затем пересекаетесь. Что, если вы сначала пересечете базу с данными, а затем сделаете из этого объединение?

kellys 18.03.2022 14:31

Не могли бы вы привести пример своего заявления? Я не уверен, что понимаю, что вы имеете в виду @kellys Редактировать: Если вы имеете в виду вот так: type TActivityServer = (IActivityServerBase & IActivityNote) | (IActivityServerBase & IActivityArchive), это не работает

Exoden 18.03.2022 14:48

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

jcalz 18.03.2022 14:59

Кажется, это известная ошибка, мс/ТС#33654. Я бы подумал, что "умная проверка типов союзов" и мс/ТС#36663 включили бы что-то подобное, но я думаю, что нет. Обходной путь выглядит как это. Это относится к вашему вопросу? Если да, то я могу написать ответ; если нет, то что мне не хватает?

jcalz 18.03.2022 15:12

@jcalz Ну да! Безопасность типа работает правильно в вашем примере, спасибо за подсказку;)

Exoden 18.03.2022 15:31
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой 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 для повышения производительности приложения путем загрузки модулей только тогда, когда они...
3
5
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это известная ошибка в TypeScript, см. Майкрософт/TypeScript#33654. До того, как TypeScript 3.5 представил более умная проверка типа объединения, это не могло работать, потому что компилятор не пытался связать тип, который он выводит для вашего вывода, с аннотированным возвращаемым типом. PR в Майкрософт/TypeScript#30779 изменил ситуацию, так что теперь есть некоторая попытка расширить типы объектов со свойствами объединения в объединения типов объектов, и PR в Майкрософт/TypeScript#36663, по-видимому, улучшает эту работу с пересечениями, но, по-видимому, ни одно из этих изменений недостаточно для заставить ваш код работать.

Пока не ясно, когда и будет ли исправлена ​​эта ошибка. На данный момент обходной путь предложено в ms/TS#33654 заключается в распространении/деструктуризации, чтобы он действительно запускал «более умную проверку типов объединения». В вашем случае это может выглядеть так:

const mapToActivityServer = (activity: TActivity): TActivityServer => {
    const { ownerId: owner, ...act } = activity;
    return { ...act, owner }; // no error now
} 

Ссылка на код для игровой площадки

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