Suggest an editImprove this articleRefine the answer for “Utility type parameters in TypeScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)`Parameters<T>` extracts the parameter types of a function as a tuple. ```typescript function greet(name: string, age: number) {} type P = Parameters<typeof greet>; // [string, number] type First = P[0]; // string ``` **Key:** always pass a type via `typeof func`, not the function value directly.Shown above the full answer for quick recall.Answer (EN)Image**Parameters<T>** - a utility type that extracts a function's parameter types as a tuple, so you can reuse them without rewriting the same signature elsewhere. ## Theory ### TL;DR - `Parameters<T>` is like photocopying a function's argument list: you get an exact copy of all input types, in the right order, with optionality intact. - `Parameters<typeof func>` turns `func(a: string, b: number)` into `[string, number]`. - It preserves order, optional markers, and rest params. Manual type aliases don't. - Use it when wrapping functions or building generic helpers. Skip it for single-param functions. ### Quick example ```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); // exact signature, no duplication }; ``` If `fetchUser` gains a new parameter, `FetchUserArgs` updates automatically. No manual sync needed. ### Key difference `Parameters<T>` returns a **tuple**, not a plain array. That means position and optionality are preserved: `[string, number?]` is not interchangeable with `[number?, string]`. When you write types by hand, you need to reconstruct this structure manually every time the source function changes. With `Parameters<T>`, the type stays in sync on its own. ### When to use - Wrapping or proxying a function: use `Parameters<T>` to match its signature exactly. - Higher-order functions and generic utilities: extract args to avoid hardcoding types. - Testing mocks: match the real function's args without copy-pasting type definitions. - API wrappers: reuse endpoint parameter types in validators or middleware. - Skip it when the function has one simple primitive param, or when you're working with non-callable types. ### How the compiler handles this `Parameters<T>` maps to a conditional type built into the TypeScript compiler: ```typescript type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never; ``` TypeScript uses `infer P` to capture the args tuple from the call signature. Rest params become variadic tuples. Optional params become `T | undefined` in the resulting tuple. No runtime cost. This is a pure type-level check. ### Common mistakes **Forgetting `typeof` on a function value:** ```typescript function add(a: number, b: number) {} type Wrong = Parameters<add>; // Error: 'add' refers to a value, not a type type Right = Parameters<typeof add>; // [number, number] ``` `Parameters` needs a type, not a value reference. Always use `typeof` when passing a function variable. **Expecting a non-empty result from a no-arg function:** ```typescript type Params = Parameters<() => void>; // [] type First = Params[0]; // never ``` No parameters means an empty tuple. Indexing into `[]` returns `never`, which sometimes surprises people who expect an error instead. **Indexing into rest params the wrong way:** ```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 params produce a single-element tuple wrapping the array type. `[0]` gives you the array, not one element of it. In practice, this is where developers most often get `Record<string, any>[]` instead of the `Record<string, any>` they expected. ### Real-world usage - **React**: `Parameters<typeof useEffect>[1]` extracts the dependency array type for custom hook wrappers. - **Express**: `Parameters<typeof getUserHandler>` reuses middleware signatures without redeclaring `Request, Response, NextFunction`. - **Zod**: Pass extracted param types into schema validators without duplicating the shape definition. - **Testing**: Wrap a spy or mock with `Parameters<typeof realFn>` so tests break if the function signature changes. ### Follow-up questions **Q:** What does `Parameters<() => void>` return? **A:** An empty tuple `[]`. There are no parameters to extract, so the result has no elements. **Q:** How does `Parameters<T>` handle optional params? **A:** Optional params become `T | undefined` in the resulting tuple. `(a?: string)` produces `[a?: string]`, which accepts both `string` and `undefined`. **Q:** What is the difference between `Parameters<T>` and writing the types manually? **A:** Manual types go stale. If the source function changes, the copy does not update. `Parameters<T>` always reflects the current signature. **Q:** Can you implement `Parameters<T>` manually? **A:** `type MyParameters<T> = T extends (...args: infer P) => any ? P : never;`. The `infer P` clause captures the args tuple from the call signature. **Q:** Does it work on arrow functions? **A:** Yes, via `typeof arrowFunc`. TypeScript infers the type from the annotation or the function body. ## Examples ### Wrapping an Express route handler ```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); // Type-safe. Change getUserHandler's signature and TypeScript reports the mismatch immediately. ``` `withAuth` accepts any handler matching `getUserHandler`'s signature. No manual type duplication. Change the params and the error surfaces right away. ### Forwarding args to a pre-configured utility ```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' }); // Second arg is still typed as string, even though formatUS ignores it ``` The tuple preserves every position. `formatUS` still expects a `string` in slot two because `FormatArgs` says so, even though the function never uses that value.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.