Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Параметри типів утиліт у TypeScript». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)`Parameters<T>` витягує типи параметрів функції у вигляді кортежу (tuple). ```typescript function greet(name: string, age: number) {} type P = Parameters<typeof greet>; // [string, number] type First = P[0]; // string ``` **Ключове:** завжди передавай тип через `typeof func`, а не саме значення функції.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Parameters<T>** - утилітний тип, який витягує типи параметрів функції у вигляді кортежу, щоб їх можна було перевикористовувати без повторного написання тієї ж сигнатури. ## Теорія ### TL;DR - `Parameters<T>` - це як ксерокс для списку аргументів функції: отримуєш точну копію всіх типів, у правильному порядку, зі збереженою опціональністю. - `Parameters<typeof func>` перетворює `func(a: string, b: number)` на `[string, number]`. - Зберігає порядок, маркери опціональності та rest-параметри. Ручні псевдоніми типів цього не роблять. - Використовуй при обгортанні функцій або побудові generic-утиліт. Для функцій з одним простим параметром сенсу немає. ### Швидкий приклад ```typescript 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: ```typescript type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never; ``` TypeScript використовує `infer P`, щоб захопити кортеж аргументів із сигнатури виклику. Rest-параметри стають варіативними кортежами. Опціональні параметри стають `T | undefined` у результуючому кортежі. Жодних витрат під час виконання. Це суто перевірка на рівні типів. ### Типові помилки **Забуваєш `typeof` для значення функції:** ```typescript function add(a: number, b: number) {} type Wrong = Parameters<add>; // Помилка: 'add' - це значення, не тип type Right = Parameters<typeof add>; // [number, number] ``` `Parameters` потребує тип, а не посилання на значення. Завжди використовуй `typeof` для змінної-функції. **Очікуєш непустий результат від функції без параметрів:** ```typescript type Params = Parameters<() => void>; // [] type First = Params[0]; // never ``` Немає параметрів - порожній кортеж. Звернення до `[]` за індексом повертає `never`, що іноді дивує тих, хто очікує помилку компіляції. **Неправильне звернення до rest-параметрів за індексом:** ```typescript 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-обробника маршруту ```typescript 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`. Ніякого ручного дублювання типів. Змінюєш параметри - помилка з'являється одразу. ### Передача аргументів у попередньо налаштовану утиліту ```typescript 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`, навіть якщо функція цей аргумент не використовує.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.