Infer в TypeScript что это и как работает

Infer typescript что это

Содержание статьи

Infer typescript что это

На практике infer применяется для извлечения возвращаемых типов функций, параметров колбэков, элементов массивов, значений Promise и частей сложных обобщённых типов. Например, с его помощью можно получить тип результата асинхронной функции без ручного дублирования сигнатур или определить тип элемента кортежа, не зная его заранее. Это снижает риск рассинхронизации типов при рефакторинге.

infer всегда работает в связке с условным типом вида T extends … ? … : …. Внутри условия описывается ожидаемая форма типа, а infer вводит временную переменную, в которую TypeScript пытается подставить подходящий тип. Если сопоставление прошло успешно, эта переменная становится доступной в положительной ветке условия.

Понимание infer важно при чтении исходников библиотек, таких как React, Redux Toolkit или TypeORM, где активно используются сложные типовые конструкции. Без знания этого механизма подобный код выглядит непрозрачным, а ошибки типов сложно диагностировать. Грамотное применение infer позволяет писать более гибкие и устойчивые типы, сохраняя строгую проверку на этапе компиляции.

Infer в TypeScript: что это и как работает

Базовый синтаксис строится вокруг конструкции T extends Pattern ? Result : Fallback, где infer используется внутри Pattern. Например, в выражении T extends Promise<infer R> ? R : never компилятор проверяет, соответствует ли T типу Promise, и при совпадении извлекает тип значения, которым он резолвится.

infer не выполняет вычислений во время исполнения кода, его работа полностью ограничена этапом проверки типов. Он не может быть использован в интерфейсах, type alias вне условных типов или в сигнатурах функций напрямую. Любая попытка применить infer вне контекста extends приведёт к ошибке компиляции.

Механизм сопоставления работает структурно: TypeScript анализирует форму типа, а не его имя. Это позволяет извлекать типы параметров функций через конструкции вида T extends (arg: infer A) => any ? A : never, а также получать типы элементов массивов и кортежей без знания их конкретной длины или содержимого.

При проектировании собственных utility-типов рекомендуется изолировать infer в узких условных выражениях и всегда предусматривать безопасную ветку else. Это снижает вероятность появления типа any или некорректного расширения типов при изменении входных данных.

Что делает ключевое слово infer внутри условных типов

Что делает ключевое слово infer внутри условных типов

Ключевое слово infer позволяет объявить типовую переменную прямо в проверяемом шаблоне условного типа. В момент проверки TypeScript пытается вывести конкретный тип, подставляя его на место infer-переменной, если входной тип структурно соответствует заданному шаблону.

Механизм работы можно свести к последовательности действий:

  • TypeScript проверяет, удовлетворяет ли исходный тип условию extends.
  • Если в условии присутствует infer, компилятор пытается вывести подходящий тип.
  • Выведенный тип становится доступен только в положительной ветке условного типа.
  • При несовпадении шаблона используется альтернативная ветка без доступа к infer-переменной.

Допускается объявление нескольких infer-переменных в одном условии, что используется при анализе сигнатур функций и кортежей:

  • извлечение типа аргумента и возвращаемого значения функции;
  • получение первого или последнего элемента кортежа;
  • разбор вложенных обобщённых типов.

Для устойчивого поведения рекомендуется:

  1. всегда задавать явную ветку else;
  2. проверять работу infer на объединениях типов, так как условные типы распределяются по union.

Как infer извлекает типы из возвращаемого значения функции

Извлечение возвращаемого типа функции с помощью infer строится на сопоставлении сигнатуры функции с шаблоном. TypeScript анализирует форму типа и, если она соответствует конструкции (...args: any[]) => infer R, помещает выведенный тип результата в переменную R.

Базовый пример такого подхода выглядит как условный тип, принимающий произвольный тип T. При совпадении с функцией возвращаемое значение становится доступным для дальнейшего использования в типовой логике. Это позволяет получить тип результата без повторного описания сигнатуры функции и без жёсткой привязки к конкретной реализации.

При работе с асинхронными функциями infer часто комбинируется с дополнительным уровнем проверки. Сначала извлекается тип, возвращаемый функцией, затем из него выделяется значение, резолвящееся из Promise. Такой подход используется для получения фактического результата async-функций на уровне типов.

Практические рекомендации включают явную проверку на функцию перед применением infer и отказ от использования any в шаблоне аргументов, если структура параметров имеет значение. Это позволяет избежать ситуаций, когда условный тип совпадает с нежелательными сигнатурами и возвращает некорректный тип.

Использование infer для получения типа элементов массива

Такой подход используется при создании универсальных utility-типов, работающих с коллекциями неизвестной структуры. Например, он позволяет определить тип значения, возвращаемого методами map, filter или пользовательскими функциями обработки массивов, не фиксируя конкретный тип данных.

Для кортежей infer также применим, но результат зависит от шаблона. Использование Array<infer T> приведёт к объединению всех типов элементов кортежа, тогда как более точные шаблоны позволяют извлекать конкретные позиции. Поэтому для массивов переменной длины предпочтительнее общий шаблон, а для кортежей – специализированные конструкции.

Как infer работает с типами кортежей и параметрами функций

При работе с кортежами infer использует позиционное сопоставление, что отличает их от обычных массивов. Кортеж имеет фиксированную длину и известные позиции элементов, поэтому шаблоны с infer могут извлекать как отдельные элементы, так и группы элементов через остаточные конструкции. Это позволяет точно определить тип первого, последнего или набора элементов без знания конкретных значений.

infer поддерживает остаточные элементы кортежей, что даёт возможность отделять фиксированную часть параметров от переменной. Такой подход полезен при описании функций с обязательными начальными аргументами и произвольным продолжением. Выведенные типы остаются строго связанными с исходной сигнатурой и автоматически обновляются при её изменении.

Извлечение вложенных типов из Promise с помощью infer

Извлечение вложенных типов из Promise с помощью infer

Механизм infer позволяет получить тип значения, на которое резолвится Promise, без необходимости вручную дублировать сигнатуру функции или тип возвращаемого значения. Основная конструкция выглядит как T extends Promise<infer R> ? R : never. TypeScript проверяет, соответствует ли T типу Promise, и при совпадении помещает тип резолва в R.

Этот приём особенно полезен для асинхронных функций и цепочек промисов, где нужно определить фактический результат выполнения функции на уровне типов. Например, для async function fetchData(): Promise<User> можно извлечь User без повторного описания типа возвращаемого значения.

При работе с вложенными или комбинированными промисами рекомендуется использовать рекурсивные условные типы, чтобы извлечь конечное значение. Это позволяет работать с Promise<Promise<T>> и более глубокими уровнями обёртки без потери информации о типе.

Для наглядности различия между исходным промисом и извлечённым типом можно представить в виде таблицы:

Исходный тип Шаблон с infer Результат
Promise<string> infer R string
Promise<number[]> infer R number[]
Promise<Promise<boolean>> infer R Promise<boolean>

Рекомендуется при работе с асинхронными цепочками явно проверять уровни вложенности, чтобы извлечённый тип соответствовал фактическому значению после полного резолва промиса. Это помогает избегать ошибок типов и сохраняет строгую типизацию при использовании сложных функций.

Ограничения и ошибки при использовании infer в TypeScript

Ограничения и ошибки при использовании infer в TypeScript

infer работает только внутри условных типов и не может быть использован вне конструкции T extends ... ? ... : .... Любая попытка применить его в интерфейсах, type alias без условия или сигнатурах функций напрямую приведёт к ошибке компиляции.

Основные ограничения и типичные ошибки включают:

  • Некорректное сопоставление шаблона. Если структура типа не соответствует ожидаемой форме, infer не срабатывает и возвращает альтернативную ветку или never.
  • Использование infer с типами, которые не поддаются структурному сопоставлению, например с примитивами без обёртки или сложными union-типами без распределения.
  • Попытка повторного использования одной infer-переменной вне области условного типа. Выведенный тип доступен только внутри ветки true.

Рекомендации по безопасному использованию infer:

  1. Всегда проверять, что входной тип соответствует ожидаемой структуре перед применением infer.
  2. Использовать узкие шаблоны и точные конструкции для кортежей, массивов и функций.
  3. Предусматривать ветку else с типом never или другим безопасным значением для случаев несовпадения.
  4. Избегать смешивания infer с обобщёнными типами без явной подстановки, чтобы не получить any в результате.

Практические примеры пользовательских utility-типов на основе infer

Практические примеры пользовательских utility-типов на основе infer

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

Этот тип извлекает тип значения, которое функция возвращает, и подходит для всех обычных и асинхронных функций. Он уменьшает дублирование при построении обёрток и декораторов.

Другой пример – извлечение типа аргументов функции в виде кортежа:

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

Такой utility-тип позволяет работать с аргументами функции программно, например, при построении прокси, логирования или передачи параметров в другой вызов без ручного описания типов.

Для работы с массивами или кортежами можно использовать utility-типы с infer для получения типа элементов:

type ElementType<T> = T extends Array<infer U> ? U : never;

Это упрощает создание функций обработки коллекций и гарантирует строгую типизацию элементов. Для кортежей можно расширять шаблон, чтобы извлекать первый элемент, остаток кортежа или конкретные позиции.

При работе с Promise полезно создавать рекурсивные типы для извлечения конечного значения:

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

Такой подход позволяет точно определить результат асинхронной функции, даже если она возвращает вложенные промисы, и используется в типах библиотек, где важно сохранить строгую проверку типов.

Вопрос-ответ:

Что делает ключевое слово infer в TypeScript и где его можно использовать?

Ключевое слово infer позволяет объявить временную типовую переменную внутри условного типа, которая будет выведена на основе структуры другого типа. Его можно применять только внутри условных типов T extends ... ? ... : .... infer позволяет извлекать типы возвращаемого значения функций, параметры функций, элементы массивов, кортежей и значения Promise. Вне условных типов использование infer приводит к ошибке компиляции.

Как с помощью infer получить тип элемента массива или кортежа?

Для массивов используется шаблон T extends Array<infer U> ? U : never. Если входной тип является массивом, TypeScript подставляет тип элемента в U. Для кортежей можно создавать более точные шаблоны, например T extends [infer First, ...infer Rest] ? First : never, чтобы получить первый элемент. Остаточные элементы кортежа тоже можно извлекать, создавая типы для дальнейшей обработки параметров или проверки структуры.

Можно ли использовать infer для извлечения типа из асинхронной функции?

Да, с помощью infer можно получить тип значения, на которое резолвится Promise. Например, T extends Promise<infer R> ? R : never позволяет извлечь тип результата асинхронной функции. Для вложенных промисов используют рекурсивные условные типы: type Awaited<T> = T extends Promise<infer R> ? Awaited<R> : T;. Это позволяет работать с функциями, возвращающими Promise<Promise<T>> и глубже.

Какие ошибки часто возникают при использовании infer и как их избежать?

Типичные ошибки включают использование infer вне условного типа, применение к типам, не подходящим под шаблон, и слишком общие конструкции, например any[] или Function. Это может привести к неверному выводу типа или к any. Чтобы избежать ошибок, следует проверять структуру входного типа, использовать точные шаблоны для кортежей и функций, а также предусматривать ветку else с безопасным значением, например never. Также рекомендуется проверять работу infer на union-типах, поскольку условные типы распределяются по каждому варианту.

Ссылка на основную публикацию