Этот вопрос проверяет понимание продвинутых возможностей системы типов TypeScript для создания гибких и зависимых от условий типов.
Conditional Types (условные типы) в TypeScript позволяют определять типы, которые выбираются на основе условия, проверяющего другие типы. Они чрезвычайно полезны для создания общих (generic) утилит, которые динамически адаптируются к переданным им типам. Классические примеры — это типы Exclude<T, U>, Extract<T, U>, NonNullable<T>, а также для определения типов возвращаемого значения функций или типов свойств в глубоко вложенных структурах.
Условные типы имеют синтаксис T extends U ? X : Y, что читается как "если тип T можно присвоить типу U, то результирующим типом будет X, иначе Y".
Создание общих utility-типов:
Многие встроенные в TypeScript утилиты реализованы через условные типы.
// Пример: тип Exclude<T, U> исключает из T все типы, которые можно присвоить U
type Exclude<T, U> = T extends U ? never : T;
type T0 = Exclude<"a" | "b" | "c", "a" | "b">; // Результат: "c"
// Пример: тип NonNullable<T> исключает null и undefined из T
type NonNullable<T> = T extends null | undefined ? never : T;
type T1 = NonNullable<string | null | undefined>; // Результат: stringПерегрузка функций в дженериках:
Условные типы позволяют выбирать тип на основе входного дженерика.
type ApiResponse<T extends { id: number }> = T extends { role: 'admin' }
? T & { adminNotes: string } // Если у T есть свойство 'role' со значением 'admin', добавляем поле
: T; // Иначе возвращаем исходный тип
type User = { id: number; name: string };
type Admin = { id: number; name: string; role: 'admin' };
type UserResponse = ApiResponse<User>; // { id: number; name: string }
type AdminResponse = ApiResponse<Admin>; // { id: number; name: string; role: 'admin'; adminNotes: string }Извлечение типов из сложных структур:
Часто используется вместе с infer для "вытаскивания" типа из обертки.
// Получаем тип возвращаемого значения функции
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type Fn = (x: number) => boolean;
type R = MyReturnType<Fn>; // type R = boolean
// Получаем тип элемента массива
type ArrayElementType<T> = T extends (infer U)[] ? U : never;
type Item = ArrayElementType<number[]>; // type Item = numberВывод: Conditional Types — это продвинутый, но невероятно мощный инструмент для создания гибких и выразительных типов, особенно в библиотеках и сложных кодовых базах. Они позволяют типам адаптироваться к условиям, что минимизирует дублирование и повышает безопасность типов.