Короткий ответ: “типа если type” в TypeScript обычно означает conditional type, то есть условный тип. Он пишется так: T extends U ? A : B. Это похоже на if, но работает не во время выполнения программы, а на этапе проверки типов
Простой пример:
type IsString<T> = T extends string ? true : false;
type A = IsString<string>;
type B = IsString<number>;
A будет true, а B будет false
Базовый синтаксис
type Result<T> = T extends условие ? если_да : если_нет;
Пример полезнее:
type ApiResponse<T> = T extends string
? { type: "text"; value: string }
: { type: "json"; value: T };
type TextResponse = ApiResponse<string>;
type UserResponse = ApiResponse<{ id: number; name: string }>;
TypeScript выбирает форму типа в зависимости от того, что передали в T
Это не обычный if
Так нельзя:
type T = if string then number;
И так тоже не работает:
const value = "hello";
type Result = value extends string ? true : false;
Типы работают с типами, а не с обычными runtime-переменными. Если нужно взять тип переменной, используйте typeof:
const value = "hello";
type Result = typeof value extends string ? true : false;
infer: достать часть типа
infer помогает извлечь тип из сложной конструкции:
type ArrayItem<T> = T extends Array<infer Item> ? Item : never;
type User = {
id: number;
name: string;
};
type Item = ArrayItem<User[]>;
Item будет User
Еще пример для Promise:
type PromiseValue<T> = T extends Promise<infer Value> ? Value : T;
type A = PromiseValue<Promise<number>>;
type B = PromiseValue<string>;
A будет number, B будет string
Когда использовать conditional types
Используйте условные типы, когда форма результата зависит от входного типа. Например:
- достать элемент массива;
- достать результат Promise;
- отфильтровать union;
- описать разные ответы API;
- сделать utility type.
Но не используйте их там, где достаточно обычного типа или union. Слишком хитрые conditional types ухудшают читаемость
Практический пример: тип ответа функции
Допустим, функция может возвращать разные данные в зависимости от режима:
type Mode = "id" | "full";
type UserResult<TMode extends Mode> = TMode extends "id"
? number
: { id: number; name: string };
function getUser<TMode extends Mode>(mode: TMode): UserResult<TMode> {
if (mode === "id") {
return 1 as UserResult<TMode>;
}
return { id: 1, name: "Dinar" } as UserResult<TMode>;
}
const id = getUser("id");
const user = getUser("full");
id будет числом, а user — объектом. Такой прием полезен, когда API функции зависит от литерального параметра
Но если пример начинает требовать много as, стоит задуматься: возможно, обычные перегрузки функции будут понятнее для команды
Частые ошибки
Первая ошибка — думать, что conditional type выполнится в runtime. Нет, это только проверка типов
Вторая ошибка — пытаться проверять значение, а не тип. Для значения нужен обычный if, для типа — T extends U ? A : B
Третья ошибка — усложнять типы без пользы. Если команда не может прочитать тип без отдельного разбора, возможно, его стоит упростить
Четвертая ошибка — забывать про распределение по union. Conditional type вида T extends string ? A : B распределяется по union. Иногда это удобно, иногда нужно отключить распределение через [T] extends [string] ? A : B
Самопроверка
Создайте тип:
type IsArray<T> = T extends unknown[] ? true : false;
Проверьте:
type A = IsArray<string[]>;
type B = IsArray<string>;
Если A стал true, а B — false, принцип “типа если type” понятен
Что почитать дальше по TypeScript
Если нужен общий маршрут по теме, откройте рубрику TypeScript. Для соседних задач пригодятся эти разборы:
- Interface и type в TypeScript: когда что использовать
- TS types: как работать с типами в TypeScript
- Union, literal types и enum в TypeScript
- Как исключить класс из типа в TypeScript



