Урок 09

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
  • Работает с массивами, кортежами, промисами
  • Рекурсивный для глубокого разбора