Параметри типів утиліт у TypeScript
Parameters
Теорія
TL;DR
Parameters<T>- це як ксерокс для списку аргументів функції: отримуєш точну копію всіх типів, у правильному порядку, зі збереженою опціональністю.Parameters<typeof func>перетворюєfunc(a: string, b: number)на[string, number].- Зберігає порядок, маркери опціональності та rest-параметри. Ручні псевдоніми типів цього не роблять.
- Використовуй при обгортанні функцій або побудові generic-утиліт. Для функцій з одним простим параметром сенсу немає.
Швидкий приклад
const fetchUser = async (id: string, options?: { cache: boolean }): Promise<User> => {
return { id, name: 'Alice' } as User;
};
type FetchUserArgs = Parameters<typeof fetchUser>;
// [id: string, options?: { cache: boolean } | undefined]
const callFetch = async (...args: FetchUserArgs) => {
return fetchUser(...args); // точна сигнатура, без дублювання
};Якщо у fetchUser з'явиться новий параметр, FetchUserArgs оновиться автоматично. Жодної ручної синхронізації.
Ключова відмінність
Parameters<T> повертає кортеж (tuple), а не звичайний масив. Це означає, що порядок і опціональність зберігаються: [string, number?] не рівнозначний [number?, string]. При ручному описі типів цю структуру доводиться відновлювати щоразу, коли змінюється вихідна функція. З Parameters<T> тип залишається актуальним сам по собі.
Коли використовувати
- Обгортання або проксування функції:
Parameters<T>для точного збігу сигнатури. - Higher-order функції та generic-утиліти: витягуй аргументи, щоб не прописувати типи вручну.
- Тестові моки: відповідність реальній сигнатурі без копіювання типів.
- API-обгортки: перевикористання типів параметрів ендпоінту у валідаторах або middleware.
- Пропускай, якщо функція має один простий параметр або ти працюєш з нефункціональним типом.
Як це обробляє компілятор
Parameters<T> - це conditional type, вбудований у компілятор TypeScript:
type Parameters<T extends (...args: any) => any> =
T extends (...args: infer P) => any ? P : never;TypeScript використовує infer P, щоб захопити кортеж аргументів із сигнатури виклику. Rest-параметри стають варіативними кортежами. Опціональні параметри стають T | undefined у результуючому кортежі. Жодних витрат під час виконання. Це суто перевірка на рівні типів.
Типові помилки
Забуваєш typeof для значення функції:
function add(a: number, b: number) {}
type Wrong = Parameters<add>; // Помилка: 'add' - це значення, не тип
type Right = Parameters<typeof add>; // [number, number]Parameters потребує тип, а не посилання на значення. Завжди використовуй typeof для змінної-функції.
Очікуєш непустий результат від функції без параметрів:
type Params = Parameters<() => void>; // []
type First = Params[0]; // neverНемає параметрів - порожній кортеж. Звернення до [] за індексом повертає never, що іноді дивує тих, хто очікує помилку компіляції.
Неправильне звернення до rest-параметрів за індексом:
const merge = (...objs: Record<string, any>[]) => ({});
type MergeArgs = Parameters<typeof merge>; // [objs: Record<string, any>[]]
type First = Parameters<typeof merge>[0]; // Record<string, any>[]Rest-параметри дають кортеж з одного елемента, який обгортає тип масиву. [0] повертає масив, а не окремий елемент. На практиці саме тут розробники найчастіше отримують Record<string, any>[] замість очікуваного Record<string, any>.
Де зустрічається в реальному коді
- React:
Parameters<typeof useEffect>[1]витягує тип масиву залежностей для обгорток кастомних хуків. - Express:
Parameters<typeof getUserHandler>дозволяє перевикористати сигнатуру middleware без повторного оголошенняRequest, Response, NextFunction. - Zod: Типи параметрів передаються у валідатори без дублювання структури схеми.
- Тести: Обгортай мок з
Parameters<typeof realFn>- тест зламається, якщо сигнатура функції зміниться.
Питання на співбесіді
Q: Що поверне Parameters<() => void>?
A: Порожній кортеж []. Немає параметрів - нема що витягувати.
Q: Як Parameters<T> підтримує опціональні параметри?
A: Опціональні параметри стають T | undefined у кортежі. (a?: string) дає [a?: string], що приймає і string, і undefined.
Q: У чому різниця між Parameters<T> і ручним описом типів?
A: Ручні типи застарівають. Якщо вихідна функція змінюється, копія не оновлюється. Parameters<T> завжди відображає актуальну сигнатуру.
Q: Чи можна реалізувати Parameters<T> вручну?
A: type MyParameters<T> = T extends (...args: infer P) => any ? P : never;. Клауза infer P захоплює кортеж аргументів із сигнатури виклику.
Q: Чи працює це зі стрілковими функціями?
A: Так, через typeof arrowFunc. TypeScript виводить тип з анотації або тіла функції.
Приклади
Обгортання Express-обробника маршруту
const getUserHandler = (req: Request, res: Response, next: NextFunction): void => {
res.json({ user: { id: req.params.id } });
};
type HandlerArgs = Parameters<typeof getUserHandler>;
// [Request, Response, NextFunction]
const withAuth = (handler: (...args: HandlerArgs) => void) => {
return (req: Request, res: Response, next: NextFunction) => {
if (req.headers.authorization) {
handler(req, res, next);
} else {
next();
}
};
};
const protectedHandler = withAuth(getUserHandler);
// Типобезпечно. Зміни сигнатуру getUserHandler - TypeScript одразу повідомить про невідповідність.withAuth приймає будь-який обробник, що відповідає сигнатурі getUserHandler. Ніякого ручного дублювання типів. Змінюєш параметри - помилка з'являється одразу.
Передача аргументів у попередньо налаштовану утиліту
const formatDate = (
date: Date,
locale: string,
options?: Intl.DateTimeFormatOptions
): string => {
return new Intl.DateTimeFormat(locale, options).format(date);
};
type FormatArgs = Parameters<typeof formatDate>;
// [date: Date, locale: string, options?: Intl.DateTimeFormatOptions]
const formatUS = (...args: FormatArgs): string => {
const [date, , options] = args;
return formatDate(date, 'en-US', options);
};
formatUS(new Date(), 'any-string', { month: 'long' });
// Другий аргумент все ще типізований як string, навіть якщо formatUS його ігноруєКортеж зберігає кожну позицію. formatUS очікує string на другій позиції, бо так сказано у FormatArgs, навіть якщо функція цей аргумент не використовує.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.