Распределяемые условные типы
Управляйте распределением условных типов по объединениям
«Распределяемые условные типы» — бесплатный урок TypeScript Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения TypeScript Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс TypeScript Academy содержит 4 уроков всего.
Неожиданное поведение
У условных типов есть особое правило: если проверяемый тип является необёрнутым параметром типа и передаётся объединение, условный тип распределяется по каждому элементу отдельно, после чего результаты объединяются.
Это основа конструкций Exclude, Extract и многих вспомогательных типов.
type ToArray<T> = T extends unknown ? T[] : never;
type A = ToArray<string | number>;
// string[] | number[] (NOT (string | number)[])Что означает «необёрнутый»
«Необёрнутый» означает, что параметр типа выступает сам по себе слева от extends, а не заключён в другой тип. Распределение происходит только для необёрнутых параметров.
type Naked<T> = T extends string ? "y" : "n";
// T is naked -> distributes over unionsРаспределение шаг за шагом
Для ToArray<string | number> компилятор обрабатывает каждый элемент:
ToArray<string>= string[]ToArray<number>= number[]
Затем объединяет их: string[] | number[].
type R = ToArray<boolean>;
// boolean is true | false, so:
// true[] | false[]Фильтрация объединения
Распределение упрощает фильтрацию. Преобразуйте ненужные элементы в never; never исчезает из объединения, оставляя только сохранённые элементы.
type Strings<T> = T extends string ? T : never;
type A = Strings<string | number | boolean>; // stringnever исчезает из объединений
Важно помнить: X | never упрощается до X. Поэтому, если распределённая ветвь даёт never, этот элемент фактически удаляется из итогового объединения.
type A = string | never; // string
type B = "a" | never | "b"; // "a" | "b"Отключение распределения
Иногда распределение не нужно, например при сравнении всего объединения как единого целого. Оберните обе стороны в кортеж из одного элемента: [T] extends [U]. Теперь T больше не является необёрнутым, поэтому распределение отключено.
type IsNever<T> = [T] extends [never] ? true : false;
type A = IsNever<never>; // true
type B = IsNever<string>; // falseПочему обёртка работает
Помещая [T] слева, Вы делаете проверяемым типом кортеж, а не отдельный параметр. Специальное правило распределения больше не применяется, поэтому объединение проверяется целиком.
type AllStrings<T> = [T] extends [string] ? true : false;
type A = AllStrings<"a" | "b">; // true
type B = AllStrings<"a" | number>; // falseРаспределяющийся и нераспределяющийся варианты
Сравним два варианта напрямую. Необёрнутый вариант проверяет каждый элемент, а обёрнутый — объединение как один тип.
type Distributed<T> = T extends string ? true : false;
type Wrapped<T> = [T] extends [string] ? true : false;
type A = Distributed<string | number>; // boolean (true | false)
type B = Wrapped<string | number>; // falseВнутреннее устройство Exclude
Встроенная конструкция Exclude<T, U> удаляет из T все элементы, совместимые с присваиванием U. Это всего лишь распределяющийся условный тип, который преобразует совпадающие элементы в never.
type MyExclude<T, U> = T extends U ? never : T;
type A = MyExclude<"a" | "b" | "c", "b">; // "a" | "c"Внутреннее устройство Extract
Extract<T, U> действует противоположным образом: сохраняет только элементы, совместимые с присваиванием U. Распределение то же, ветви противоположные.
type MyExtract<T, U> = T extends U ? T : never;
type A = MyExtract<"a" | "b" | "c", "a" | "c">; // "a" | "c"Практическая сила
Распределение позволяет преобразовать каждый элемент объединения одним выражением: создать массив для каждого элемента, добавить префикс к каждой строке или отфильтровать элементы по структуре. В сочетании с [T] extends [U] для проверки всего объединения Вы точно контролируете, когда происходит распределение.
Обозначения: в настоящем TypeScript типы шаблонных строк записываются строками в обратных кавычках, содержащими места для подстановки в формате «доллар — фигурные скобки». В этих фрагментах мы показываем такой шаблон как Tpl<...>, перечисляя все его части по порядку; например, шаблон в обратных кавычках, соответствующий сначала литеральному префиксу, а затем Rest, выглядит как Tpl<'prefix', infer Rest>.
type Prefix<T extends string> = T extends unknown ? Tpl<'id_', T> : never;
// Tpl<'id_', T> is the template literal type joining 'id_' with each T
type A = Prefix<'a' | 'b'>; // 'id_a' | 'id_b'Быстрая проверка
Проверьте, насколько хорошо Вы понимаете распределяющиеся условные типы.
Итоги
Распределяющиеся условные типы лежат в основе инструментов для работы с объединениями.
- Необёрнутый
T extends U ?распределяется по каждому элементу объединения. - Ветви, возвращающие
never, удаляют элементы. [T] extends [U]отключает распределение при проверке всего объединения.ExcludeиExtract— небольшие распределяющиеся условные типы.
Далее, в курсе 22: арифметика в системе типов.
Часто задаваемые вопросы
Урок «Распределяемые условные типы» бесплатный?
Да — полный текст урока «Распределяемые условные типы» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс TypeScript Academy, подпишись на CoddyKit PRO. Курс TypeScript Academy содержит 4 уроков всего.
Чему я научусь в уроке «Распределяемые условные типы»?
Управляйте распределением условных типов по объединениям Ты практикуешь TypeScript Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать TypeScript Academy?
Предыдущий опыт не требуется. TypeScript Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Распределяемые условные типы»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке TypeScript Academy?
Да. Каждый урок TypeScript Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Типы как язык вычислений
- Условные типы на уровне типов
- Рекурсия на уровне типов
- Распределяемые условные типы