TypeScript: извлечь, а затем исключить из союза

Допустим, у меня есть тип союза Vehicle, который выглядит так:

interface Car {
  color: string;
  doors: number;
  wheels: number;
}

interface Plane {
  color: string;
  wings: number;
}

type Vehicle = Car | Plane;

Я хочу (1) извлечь из этого объединения типы, которые расширяют аргумент данного типа (это может быть легко достигнуто с помощью служебного типа Extract) и (2) исключить из извлеченного типа(ов) ключи того же аргумента типа . Вот желаемый результат:

type Result1 = ExtractOmit<{ wheels: number; }>;
// This should give me the result of Omit<Car, "wheels">:
// { color: string; doors: number; }

type Result2 = ExtractOmit<{ wings: number; }>;
// This should give me the result of Omit<Plane, "wings">:
// { color: string; }

type Result3 = ExtractOmit<{ foo: number; }>;
// This should produce an error since foo is neither in Car nor Plane

Вот неудачная попытка:

type ExtractOmit<T> = Omit<Extract<Vehicle, T>, keyof T>;
// T extends what?

Правая часть кажется мне правильной, но я не могу понять, как поставить левую.

Любое предложение?

Площадка 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 для повышения производительности приложения путем загрузки модулей только тогда, когда они...
1
0
24
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
interface Car {
    color: string;
    doors: number;
    wheels: number;
}

interface Plane {
    color: string;
    wings: number;
}

type Vehicle = Car | Plane;

type OmitExtract<T extends Partial<Vehicle>> = Omit<Extract<Vehicle, T>, keyof T>;

type Result = OmitExtract<{ wheels: number; }>;

type Result1 = OmitExtract<{ wheels: number; }>;
// This should give me the result of Omit<Car, "wheels">:
// { color: string; doors: number; }

type Result2 = OmitExtract<{ wings: number; }>;
// This should give me the result of Omit<Plane, "wings">:
// { color: string; }

Вышеупомянутое, похоже, работает так, как ожидалось.

Ключевыми элементами здесь являются использование «Автомобиля» (я заметил, что вы использовали Элемент ранее) и наличие T, расширяющего Partial слева, чтобы дать ему супертип, необходимый для извлечения, чтобы знать, из чего правильно извлекаться.

Обновлено: ссылка на игровую площадку

На самом деле вам даже не нужен «T extends Partial<Vehicle>», если только вы действительно не хотите ограничить ввод супертипа T. Наличие транспортного средства справа — это то, что наиболее важно для настройки типа утилиты Extract.

Azarro 07.05.2022 11:13

Вам нужно использовать type Vehicle = Car | Plane; согласно сообщению ОП. Оригинальная игровая площадка OP имела неправильный код с type Vehicle = Car & Plane, что давало неправильный результат, как на вашей игровой площадке. Union — это то, что необходимо в Vehicle для правильной настройки Vehicle.

Azarro 07.05.2022 11:14

Спасибо! Да, извините за путаницу с Element, я имел в виду Vehicle, как вы догадались.

tavoyne 07.05.2022 11:14

Я заметил, что есть один пограничный случай, который не охвачен этим. Это когда { color: string; } передается в качестве аргумента. Поскольку и Car, и Plane расширяют { color: string; }, я ожидаю, что результатом будет объединение тех (из которых color будет опущено): { doors: number; wheels: number; } | { wings: number; }. Но все, что я получаю, это {}. Любая идея, как мы могли бы принять этот случай во внимание?

tavoyne 07.05.2022 11:41

Все, что нам нужно, это дистрибутив Omit (см. stackoverflow.com/questions/57103834/…).

tavoyne 07.05.2022 11:47
typescriptlang.org/play?#code/…
tavoyne 07.05.2022 11:49

Как раз собирался опубликовать то же самое после того, как наткнулся на поток stackoverflow! Надеюсь, это сработало. Сегодня мы узнали намного больше о типах полезностей ТС, ха-ха.

Azarro 07.05.2022 11:52

В общем, я обнаружил, что есть много мест, в которых машинописный текст не допускает хорошего разделения Union (т.е. имеет дело с явным «A | B» как типом, когда дело доходит до проверки равенства/присваивания).

Azarro 07.05.2022 11:53

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