Что такое infer в TypeScript?

SeniorTypeScript · Frontend·Обновлено 14 июля 2026
Коротко
infer — ключевое слово в условных типах TypeScript, которое позволяет «вытащить» (вывести) часть типа в новую переменную типа прямо внутри выражения extends.

Что такое infer

infer используется исключительно внутри условных типов (conditional types) в позиции extends. Оно объявляет временную переменную типа, которую TypeScript заполняет в момент проверки условия. Если условие выполняется — переменная доступна в ветке true.

Синтаксис:

T extends SomeType<infer U> ? U : never

Здесь U — выведенный тип. Если T соответствует SomeType<...>, то U будет равен аргументу этого дженерика.

Зачем это нужно

Без infer невозможно написать утилитарные типы, которые «разбирают» чужой тип на части. Именно на infer построены встроенные утилиты TypeScript: ReturnType, Parameters, InstanceType, Awaited.

Практические примеры

Извлечение возвращаемого типа функции

Встроенный ReturnType<T> реализован так:

type ReturnType<T extends (...args: any) => any> =
  T extends (...args: any) => infer R ? R : never;

TypeScript смотрит: «Является ли T функцией? Если да — выведи тип возврата в R».

Извлечение типа элемента массива

type ElementOf<T> = T extends Array<infer Item> ? Item : never;
type A = ElementOf<string[]>; // string
type B = ElementOf<number[]>;  // number

Раскрытие вложенного Promise

Механика Awaited выглядит примерно так:

type Awaited<T> =
  T extends Promise<infer V> ? Awaited<V> : T;
type R = Awaited<Promise<Promise<number>>>; // number

Извлечение типов параметров функции

type FirstParam<T extends (...args: any[]) => any> =
  T extends (first: infer F, ...rest: any[]) => any ? F : never;

function greet(name: string, age: number) {}
type N = FirstParam<typeof greet>; // string

Ограничения и нюансы

  • infer работает только внутри extends в условном типе — нигде больше.
  • Одна и та же переменная infer может появляться в нескольких местах; тогда TypeScript пытается вывести пересечение или объединение типов в зависимости от позиции (ковариантная / контравариантная).
  • В контравариантных позициях (параметры функций) несколько infer U образуют пересечение (&), в ковариантных — объединение (|).
  • infer нельзя использовать вне дистрибутивных условных типов без обёртки в кортеж: [T] extends [infer U] — способ отключить дистрибутивность.

Дистрибутивность и infer

Когда T — голый тип-параметр, условный тип раскрывается по каждому члену объединения:

type Flatten<T> = T extends Array<infer U> ? U : T;
type R = Flatten<string[] | number[]>; // string | number

Что хочет услышать интервьюер

Кандидат объясняет, что `infer` работает только внутри условных типов в позиции `extends` и позволяет объявить переменную типа «на лету»

Знание встроенных утилит, построенных на `infer`: `ReturnType`, `Parameters`, `Awaited`, `InstanceType`

Понимание дистрибутивности условных типов и того, как она взаимодействует с `infer`

Умение написать собственный утилитарный тип с `infer` — например, извлечь тип элемента массива или первый аргумент функции

Знание о поведении `infer` в ковариантных и контравариантных позициях (объединение vs пересечение)

Пример: Базовый пример: извлечение типа возврата

// Собственная реализация ReturnType
type MyReturnType<T extends (...args: any[]) => any> =
  T extends (...args: any[]) => infer R ? R : never;

function fetchUser(): Promise<{ id: number; name: string }> {
  return Promise.resolve({ id: 1, name: 'Alice' });
}

type UserResponse = MyReturnType<typeof fetchUser>;
// Promise<{ id: number; name: string }>

Пример: Извлечение типа элемента массива или кортежа

type ElementOf<T> = T extends ReadonlyArray<infer Item> ? Item : never;

type Str = ElementOf<string[]>;          // string
type Num = ElementOf<readonly number[]>; // number
type Mixed = ElementOf<[string, number]>; // string | number

Пример: Рекурсивное раскрытие вложенных Promise (аналог Awaited)

type DeepAwaited<T> =
  T extends Promise<infer Inner>
    ? DeepAwaited<Inner>
    : T;

type A = DeepAwaited<Promise<Promise<Promise<string>>>>; // string
type B = DeepAwaited<number>;                           // number

Пример: Контравариантная позиция: пересечение типов

// В позиции параметра функции infer формирует пересечение
type UnionToIntersection<U> =
  (U extends any ? (x: U) => void : never) extends (x: infer I) => void
    ? I
    : never;

type Result = UnionToIntersection<{ a: string } | { b: number }>;
// { a: string } & { b: number }

Типичные ошибки

Попытка использовать `infer` вне условного типа (например, в обычном дженерике) — это синтаксическая ошибка

Путаница между `infer` и обычным параметром дженерика: кандидат не понимает, зачем нужен `infer`, если можно просто передать тип аргументом

Незнание, что `infer` в нескольких позициях одного условного типа может давать пересечение (контравариантная позиция) вместо ожидаемого объединения

Забывают про дистрибутивность: не знают, как с помощью кортежа `[T] extends [infer U]` её отключить

Не могут назвать ни одного встроенного типа, реализованного через `infer`, хотя пользуются `ReturnType` каждый день

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.5
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее