infer и извлечение типов
infer объявляет переменную типа внутри условия extends. Используется для извлечения частей типа.
Базовый пример
type ElementType<T> = T extends (infer U)[] ? U : never;
type A = ElementType<string[]>; // string
type B = ElementType<number[]>; // number
infer U — если T — массив, извлечь тип элемента.
ReturnType
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type A = ReturnType<() => string>; // string
type B = ReturnType<(x: number) => void>; // void
Parameters
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
type A = Parameters<(a: number, b: string) => void>;
// [number, string]
Promise
type Unwrap<T> = T extends Promise<infer U> ? U : T;
type A = Unwrap<Promise<string>>; // string
type B = Unwrap<number>; // number
Первый элемент кортежа
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;
type A = First<[string, number, boolean]>; // string
Последний элемент
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;
type A = Last<[string, number, boolean]>; // boolean
Извлечение по шаблону
type GetProp<T, K extends keyof T> = T extends { [P in K]: infer V } ? V : never;
Несколько infer
type UnwrapPromise<T> = T extends Promise<infer U> ? UnwrapPromise<U> : T;
in conditional type
type IsArray<T> = T extends any[] ? true : false;
type ArrayElement<T> = T extends (infer U)[] ? U : never;
Пример: extract props
type PropsOf<T> = T extends React.Component<infer P> ? P : never;
Пример: map async
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
type A = Awaited<Promise<Promise<string>>>; // string
Пример: extract function return
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type UnpackArray<T> = T extends (infer U)[] ? U : T;
type UnpackFunction<T> = T extends (...args: any[]) => infer R ? R : T;
Рекурсивный infer
type Flatten<T> = T extends (infer U)[] ? Flatten<U> : T;
type A = Flatten<string[][]>; // string
type B = Flatten<number[][][]>; // number
Практика: extract event type
type EventType<T> = T extends { type: infer U } ? U : never;
type LoginEvent = { type: 'login'; userId: number };
type LogoutEvent = { type: 'logout'; userId: number };
type A = EventType<LoginEvent>; // 'login'
type B = EventType<LogoutEvent>; // 'logout'
Практика: extract schema type
type InferSchema<T> = T extends { parse: (input: unknown) => infer U } ? U : never;
Используется в Zod: z.infer<typeof Schema>.
Ограничения
inferтолько внутриextends- Нельзя использовать в теле типа
- Несколько
infer— по порядку
Итоги
inferизвлекает тип- Внутри
T extends X<infer U> ? ... - Основа
ReturnType,Parameters,Awaited - Работает с массивами, кортежами, промисами
- Рекурсивный для глубокого разбора