Skip to main content

Як дістати тип аргументів функції через infer?

TypeScript дозволяє дістати тип аргументів функції за допомогою ключового слова infer усередині умовних типів. Це один із найчастіших і найкорисніших кейсів використання infer.


Загальна ідея

Ми перевіряємо, що якийсь тип T - це функція, і кажемо TypeScript:

"Якщо T - це функція, аргументи якої мають тип A, то виведи цей A."


Приклад 1: Дістати тип одного аргументу

javascript
type FirstArg<T> = T extends (arg: infer A) => any ? A : never; type Example = (x: number) => void; type Result = FirstArg<Example>; // number

Тут:

  • T extends (arg: infer A) => any - перевірка, чи є T функцією.
  • infer A - TypeScript "витягує" тип першого аргументу.
  • Якщо T не функція, результатом буде never.

Приклад 2: Дістати всі аргументи функції у вигляді кортежу

javascript
type Args<T> = T extends (...args: infer A) => any ? A : never; type Fn = (x: number, y: string) => boolean; type Params = Args<Fn>; // [number, string]

infer A у цьому випадку стає кортежем типів аргументів.


Приклад 3: Дістати тип значення, яке повертається

javascript
type Return<T> = T extends (...args: any[]) => infer R ? R : never; type Fn = (x: number, y: string) => boolean; type R = Return<Fn>; // boolean

Це - реалізація вбудованого типу ReturnType<T>.


Приклад 4: Отримати і аргументи, і результат

javascript
type FunctionParts<T> = T extends (...args: infer A) => infer R ? { args: A; return: R } : never; type Fn = (id: string, active: boolean) => number; type Parts = FunctionParts<Fn>; /* { args: [string, boolean]; return: number; } */

Приклад 5: Перший елемент аргументів

javascript
type FirstArg<T> = T extends (...args: [infer First, ...any[]]) => any ? First : never; type Fn = (name: string, age: number) => void; type A = FirstArg<Fn>; // string

Тут використовується розпакування кортежів: [infer First, ...any[]].


Приклад 6: Останній аргумент

javascript
type LastArg<T> = T extends (...args: [...any[], infer Last]) => any ? Last : never; type Fn = (a: number, b: string, c: boolean) => void; type L = LastArg<Fn>; // boolean

Приклад 7: Тип аргументів у методу класу

javascript
class User { save(id: number, active: boolean) {} } type MethodArgs = Args<User["save"]>; // [number, boolean]

Працює для будь-яких методів - просто беремо тип за ключем ["methodName"] і застосовуємо утиліту.


Приклад 8: Реалізація аналога вбудованого Parameters<T>

TypeScript уже містить готовий утилітний тип:

javascript
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer A) => any ? A : never; type Fn = (x: number, y: string) => boolean; type Params = MyParameters<Fn>; // [number, string]

Це еквівалент стандартного Parameters<Fn>.


Приклад 9: Отримати тип аргументів callback-функції

javascript
type Callback = (err: Error | null, data: string) => void; type CallbackArgs = Args<Callback>; // [Error | null, string]

Часто використовується в типізації API, Node.js колбеків, React-хуків тощо.


Приклад 10: Застосувати аргументи до іншої функції

javascript
type Args<T> = T extends (...args: infer A) => any ? A : never; function call<T extends (...args: any[]) => any>(fn: T, ...args: Args<T>) { return fn(...args); } function greet(name: string, age: number) { return `${name}, ${age}`; } call(greet, "Tim", 25); // ok call(greet, 25, "Tim"); // помилка типів

Тут Args<T> гарантує правильний порядок і типи аргументів.


Підсумок

Що дістаємоПрикладРезультат
Один аргументT extends (a: infer A) => anyтип першого аргументу
Всі аргументиT extends (...args: infer A) => anyкортеж аргументів
ПоверненняT extends (...args: any[]) => infer Rтип результату
Перший аргументT extends (...args: [infer F, ...any[]]) => anyперший аргумент
Останній аргументT extends (...args: [...any[], infer L]) => anyостанній аргумент

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.