Условные типы
Условный тип — это тернарный оператор на уровне типов: T extends U ? X : Y.
Базовый пример
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
type C = IsString<'hello'>; // true
Проверка на массив
type IsArray<T> = T extends any[] ? true : false;
type A = IsArray<string[]>; // true
type B = IsArray<string>; // false
Выбор типа
type ElementType<T> = T extends (infer U)[] ? U : T;
type A = ElementType<string[]>; // string
type B = ElementType<number[]>; // number
type C = ElementType<boolean>; // boolean
infer U извлекает тип элемента массива.
С infer
infer объявляет переменную типа внутри условия:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type A = ReturnType<() => string>; // string
type B = ReturnType<(x: number) => void>; // void
Распространение на union
Условные типы распределяются по union:
type NonNullable<T> = T extends null | undefined ? never : T;
type A = NonNullable<string | null>; // string
type B = NonNullable<string | null | number>; // string | number
Каждый элемент union проверяется отдельно, результаты объединяются.
Отключение распределения
Обернуть T в [T]:
type IsNever<T> = [T] extends [never] ? true : false;
type IsUnion<T> = T extends T ? ([T] extends [T] ? false : true) : never;
Вложенные условия
type TypeName<T> =
T extends string ? 'string' :
T extends number ? 'number' :
T extends boolean ? 'boolean' :
T extends any[] ? 'array' :
'other';
type A = TypeName<string>; // 'string'
type B = TypeName<number>; // 'number'
type C = TypeName<string[]>; // 'array'
Практика: Unwrap
type Unwrap<T> = T extends Promise<infer U> ? U : T;
type A = Unwrap<Promise<string>>; // string
type B = Unwrap<number>; // number
Вложенные промисы:
type DeepUnwrap<T> = T extends Promise<infer U> ? DeepUnwrap<U> : T;
type A = DeepUnwrap<Promise<Promise<string>>>; // string
Практика: FunctionKeys
type FunctionKeys<T> = {
[K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
type User = {
name: string;
age: number;
greet: () => void;
logout: () => void;
};
type Fns = FunctionKeys<User>;
// 'greet' | 'logout'
Практика: RequiredKeys
type RequiredKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];
Проверка на never
type IsNever<T> = [T] extends [never] ? true : false;
type A = IsNever<never>; // true
type B = IsNever<string>; // false
Без [T] распределение сломает проверку.
Итоги
T extends U ? X : Y— условный типinferизвлекает тип- Распространение на union автоматическое
[T]отключает распространение- Основа для утилит вроде
ReturnType,Awaited