Utility type parameters in TypeScript
Parameters
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>turnsfunc(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
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:
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:
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:
type Params = Parameters<() => void>; // []
type First = Params[0]; // neverNo 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:
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 redeclaringRequest, 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
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
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 itThe tuple preserves every position. formatUS still expects a string in slot two because FormatArgs says so, even though the function never uses that value.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.