0Pricing
TypeScript Academy · Урок

Распределяемые условные типы

Управляйте распределением условных типов по объединениям

«Распределяемые условные типы» — бесплатный урок 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>; // string

never исчезает из объединений

Важно помнить: 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 — локальная установка не требуется.

Все уроки этого курса

  1. Типы как язык вычислений
  2. Условные типы на уровне типов
  3. Рекурсия на уровне типов
  4. Распределяемые условные типы
← Назад к TypeScript Academy