Infer: извлечение типов
infer— ключевое слово TypeScript для использования внутри conditional types: объявляет типовую переменную, которую компилятор должен вывести (infer) из сопоставляемого типа.
Зачем нужно
infer позволяет «вытащить» часть типа из сложной структуры без явного указания — TypeScript сам вычисляет нужный фрагмент. Это основа для написания продвинутых утилитарных типов вроде ReturnType, Parameters, Awaited, UnpackPromise.
Где используется
- Реализация стандартных утилит:
ReturnType,Parameters,InstanceType,Awaited - Извлечение типа элемента массива, аргументов функции, разрешённого типа промиса
- Парсинг строковых шаблонных литералов на уровне типов
- Тип-уровневые трансформации в сложных библиотеках
Основной контент
Синтаксис infer
infer используется только в ветке extends conditional type. Переменная, объявленная через infer, доступна только в ветке true.
// Общая форма:
type MyType<T> = T extends SomePattern<infer U> ? U : FallbackType;
Извлечение типа возвращаемого значения
// Аналог ReturnType из стандартной библиотеки
type MyReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R ? R : never;
function greet(name: string): string {
return `Hello, ${name}`;
}
type Result = MyReturnType<typeof greet>; // string
Извлечение типов параметров
type MyParameters<T extends (...args: any) => any> =
T extends (...args: infer P) => any ? P : never;
function add(a: number, b: number): number {
return a + b;
}
type Params = MyParameters<typeof add>; // [a: number, b: number]
Извлечение типа элемента массива
type ArrayElement<T> = T extends (infer U) ? U : never;
type E1 = ArrayElement<string>; // string
type E2 = ArrayElement<(number | boolean)>; // number | boolean
type E3 = ArrayElement<string>; // never — не массив
Извлечение типа промиса
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type P1 = UnwrapPromise<Promise<string>>; // string
type P2 = UnwrapPromise<number>; // number (не промис)
Несколько infer в одном conditional type
// Первый и последний элементы кортежа
type Head<T extends any> = T extends [infer H, ...any] ? H : never;
type Tail<T extends any> = T extends [...any, infer L] ? L : never;
type H = Head<[1, 2, 3]>; // 1
type L = Tail<[1, 2, 3]>; // 3
// Разделение аргументов функции
type FirstArg<T> = T extends (first: infer F, ...rest: any) => any ? F : never;
function process(id: number, name: string): void {}
type FArg = FirstArg<typeof process>; // number
Строковые шаблоны с infer (TS 4.1+)
type GetEventName<T extends string> =
T extends `on${infer Event}` ? Event : never;
type Click = GetEventName<"onClick">; // "Click"
type Change = GetEventName<"onChange">; // "Change"
type None = GetEventName<"value">; // never
Частые ошибки
- Использовать
inferвне conditional type —inferработает только внутриT extends ... ? ... : .... - Использовать
inferв false-ветке — переменнаяinfer Uдоступна только в true-ветке (? U), не в false. - Забыть ограничить T перед infer — без
T extends (...args: any) => anyвывод параметров функции не сработает. - Смешивать infer с обычными generic-параметрами —
inferобъявляет новую переменную, не переиспользует внешние.
Связанные темы
- Conditional types
- ReturnType, Parameters, InstanceType
- Awaited -- тип для промисов
- Mapped types
- _MOC TypeScript