Что такое infer в typescript и как использовать для вывода типов

Погружение в TypeScript: что такое infer и как его использовать правильно

Зачем вообще нужен infer?

Если ты уже немного знаком с TypeScript, то наверняка слышал о возможности "выводить" типы. Но когда дело доходит до сложных ситуаций — например, извлечения типа из функции или массива — обычный вывод типа становится недостаточным. Вот здесь и приходит на помощь ключевое слово infer.

Проще говоря, infer позволяет внутри условного типа временно «ввести» новый тип и использовать его в логике определения. Это мощный инструмент для создания динамических типов, который позволяет буквально "доставать" нужную часть из структуры.

Как работает infer в TypeScript

На практике infer используется внутри условных типов (conditional types). Синтаксис выглядит примерно так:

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

В этом примере мы создаём тип, который получает тип возвращаемого значения функции. Здесь:
- `` — универсальный параметр типа,
- `T extends (...args: any[]) => infer R` — проверяем, является ли T функцией, и если да, то извлекаем тип её возвращаемого значения в переменную `R`,
- `? R : never` — если условие выполнено, вернуть R, иначе вернуть `never`.

Диаграмма: визуализация infer

Что такое infer в TypeScript - иллюстрация

Представим себе следующую диаграмму в виде текста:

```
Функция: (x: number) => string
|
v
Применяем условный тип:
T extends (...args: any[]) => infer R
|
v
R = string
```

На выходе мы получаем тип `string`, который был извлечён из сигнатуры функции при помощи infer.

Пример infer в TypeScript: разбираем по полочкам

Давай разберём простой, но показательный пример:

```ts
type FirstElement = T extends [infer First, ...any[]] ? First : never;

type A = FirstElement<[number, string, boolean]>; // number
type B = FirstElement<[]>; // never
```

Что здесь происходит:
- Мы проверяем, является ли тип массивом с хотя бы одним элементом,
- Если да — извлекаем первый элемент и сохраняем его как тип `First`,
- Если нет — возвращаем `never`.

Вот и наглядный пример использования infer в TypeScript, где типы начинают "работать" с содержимым, а не просто проверяют его форму.

Частые ошибки новичков при использовании infer

Переходим к самому интересному — где новички чаще всего спотыкаются.

- Ожидание, что infer будет работать сам по себе. Это не магия — infer работает только внутри условных типов.
- Попытка использовать infer вне контекста `extends`. Например, вот так нельзя:

```ts
// Ошибка!
type Wrong = infer T;
```

- Забывание про альтернативный путь (`: never`). Если не указать, что делать в противном случае, TypeScript выдаст вам ошибку.

Кроме того, многие забывают, что infer не может напрямую использоваться в теле функции — он исключительно "типовая" конструкция.

Сравнение: infer и обычное присваивание типов

Обычно, когда ты пишешь `const x: number = 5`, ты явно указываешь тип. Или же TypeScript сам выводит его как `number`. Но с infer всё иначе: он применяется в обобщённых, универсальных контекстах, где простой вывод типа невозможен.

Вот ключевые различия:
- infer работает на этапе компиляции с типами, не с данными.
- Он позволяет создавать гибкие типы, адаптирующиеся к входным параметрам.
- Это не альтернатива `typeof` или `instanceof` — это инструмент типов, а не значений.

Почему infer — это мощь, но с нюансами

Понимание, как работает infer в TypeScript, открывает двери к созданию настоящих "интеллектуальных" типов. С его помощью можно:
- Извлекать типы аргументов и возвращаемых значений функций,
- Работать с вложенными структурами,
- Строить цепочки типовых преобразований.

Несколько практических применений

Вот примеры, где использование infer в TypeScript оправдано на 100%:

- Типизация API-ответов: Извлечение полезной нагрузки из обёрнутых типов вроде `Promise>`.
- Переиспользование логики в разных типах: Например, извлечение типа элемента массива или поля из интерфейса.

Маркируем это:

- Улучшение читаемости типовой логики
- Снижение дублирования
- Повышение типовой безопасности

На заметку: инструкция по применению infer

Если хочешь начать использовать infer в TypeScript правильно, вот краткий путеводитель:

- Всегда используй его внутри условного типа.
- Дай извлечённому типу имя, которое будет использоваться в логике.
- Продумывай, что делать, если условие не выполнено — не забывай про `: never`.

Заключение

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

Поэтому, изучая, что такое infer в TypeScript, не забывай тестировать свои типы, проверять граничные случаи и не бояться экспериментировать. Именно так приходит настоящее понимание.

Прокрутить вверх