ПрограммированиеTypeScript/Fullstack разработчик

Как работают Conditional Types с infer в TypeScript, в каких ситуациях они нужны, и какие ошибки встречаются на практике?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

Conditional Types с использованием ключевого слова infer позволяют извлекать типы из сложных типов данных. Классический пример — извлечение типа элемента из массива:

type ElementType<T> = T extends (infer U)[] ? U : T;

Здесь infer U позволяет вычислить тип элемента массива T. Если T — массив, вернется тип его элементов, иначе — сам T.

Где используются:

  • Для написания универсальных helper-типов (например, извлечение return type из функции, типа значения Promise и т.д.).

Пример:

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

Вопрос с подвохом.

Можно ли использовать несколько infer в одном условном типе? Как интерпретирует TypeScript этот случай?

Неправильный ответ:

  • "Нельзя использовать больше одного infer, TypeScript не даст этого сделать."

Правильный ответ:

  • Можно использовать несколько переменных infer в одном шаблоне. Например, разбирая кортеж:
type FirstArgument<T> = T extends (infer F, ...any[]) => any ? F : never; // Но корректнее с функциями: type Args<T> = T extends (...args: infer A) => any ? A : never;
  • TypeScript правильно выводит несколько типов через разные переменные infer.

Примеры реальных ошибок из-за незнания тонкостей темы.


История

Разработчик написал способ для получения типа объекта, возвращаемого функцией, но не учёл, что функция может возвращать Promise. В итоге, тип возвращаемого значения был всегда Promise<any>, так как не использовался вложенный conditional с extract/infer. Пришлось рефакторить код по всей базе.


История

В проекте был введён универсальный тип для распаковки вложенных массивов, но забыли прописать конечный else-branch в условии. TypeScript корректно не ошибался, но при использовании в некоторых случаях результат был never, из-за чего ломались некоторые utility-типизации во внешних библиотеках.


История

Коллега попытался извлечь типы свойств у большого интерфейса через combine-conditional/infer-тип, не учёл, что некоторые свойства были themselves union-типами. В результате были неожиданные комбинации union-типов на выходе, компилятор пропускал, а логика работала некорректно.