Як дістати тип аргументів функції через infer?
TypeScript дозволяє дістати тип аргументів функції за допомогою ключового слова infer усередині умовних типів.
Це один із найчастіших і найкорисніших кейсів використання infer.
Загальна ідея
Ми перевіряємо, що якийсь тип T - це функція, і кажемо TypeScript:
"Якщо
T- це функція, аргументи якої мають типA, то виведи цейA."
Приклад 1: Дістати тип одного аргументу
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: Дістати всі аргументи функції у вигляді кортежу
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: Дістати тип значення, яке повертається
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: Отримати і аргументи, і результат
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: Перший елемент аргументів
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: Останній аргумент
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: Тип аргументів у методу класу
class User {
save(id: number, active: boolean) {}
}
type MethodArgs = Args<User["save"]>;
// [number, boolean]Працює для будь-яких методів - просто беремо тип за ключем
["methodName"]і застосовуємо утиліту.
Приклад 8: Реалізація аналога вбудованого Parameters<T>
TypeScript уже містить готовий утилітний тип:
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-функції
type Callback = (err: Error | null, data: string) => void;
type CallbackArgs = Args<Callback>; // [Error | null, string]Часто використовується в типізації API, Node.js колбеків, React-хуків тощо.
Приклад 10: Застосувати аргументи до іншої функції
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 | останній аргумент |
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.