Skip to content
TypeScript ts utility-types 3 min read

Parameters & ReturnType

Parameters<T> and ReturnType<T> extract the input and output types of a function type, so you can derive new types from a function instead of duplicating its signature by hand. Both are built on conditional types with the infer keyword, making them precise even for overloads, generics, and rest parameters. They are the go-to tools when you need to wrap, proxy, memoize, or test a function while staying perfectly in sync with its real shape. This page shows their usage, how they are implemented, and the common pitfalls.

Parameters: the argument tuple

Parameters<T> returns a tuple of a function’s parameter types. The result is an ordered tuple, so you can index into it, spread it, or destructure it.

function createUser(name: string, age: number, admin = false) {
  return { name, age, admin };
}

type Args = Parameters<typeof createUser>;
// [name: string, age: number, admin?: boolean]

type First = Args[0]; // string

Note the use of typeof createUser to turn the value into its function type. The tuple even preserves parameter names (as labels) and optionality, which is why admin appears as admin?: boolean.

ReturnType: the output type

ReturnType<T> extracts whatever a function returns. Combined with Awaited, it is perfect for typing the resolved value of an async function.

function getConfig() {
  return { host: "localhost", port: 5432 };
}

type Config = ReturnType<typeof getConfig>;
// { host: string; port: number }

async function fetchUser() {
  return { id: 1, name: "Ada" };
}

type User = Awaited<ReturnType<typeof fetchUser>>;
// { id: number; name: string }

For fetchUser, ReturnType alone gives Promise<{ id; name }>; wrapping it in Awaited unwraps the promise to the underlying value.

How they are implemented

Both are short conditional types that use infer to capture a piece of the function signature and fall back to a default when the input is not a function.

type MyParameters<T extends (...args: any) => any> =
  T extends (...args: infer P) => any ? P : never;

type MyReturnType<T extends (...args: any) => any> =
  T extends (...args: any) => infer R ? R : any;

infer P binds the entire parameter list to a tuple P; infer R binds the return type. The extends (...args: any) => any constraint ensures only function types are accepted.

Using them together to wrap a function

The classic use case is forwarding arguments to a function while changing or observing the result. Pairing the two utilities keeps the wrapper exactly aligned with the original.

function withLogging<T extends (...args: any[]) => any>(fn: T) {
  return (...args: Parameters<T>): ReturnType<T> => {
    console.log("args:", args);
    return fn(...args);
  };
}

const logged = withLogging(createUser);
logged("Ada", 36); // ✅ same signature as createUser
logged("Ada");     // ❌ Error: expected 2-3 args, got 1

Because args is typed as Parameters<T> and the return as ReturnType<T>, the wrapped function is indistinguishable from the original to callers, including arity checks.

Comparison

UtilityInputOutputBuilt with
Parameters<T>function typetuple of param typesinfer P on the args
ReturnType<T>function typethe return typeinfer R on the result

On overloaded functions, both utilities resolve against the last overload signature only. If you need a different overload, extract it explicitly rather than relying on these utilities.

Best Practices

  • Use typeof fn to feed a value into Parameters/ReturnType; they require a function type.
  • Wrap ReturnType in Awaited to get the resolved type of an async function.
  • Type forwarding wrappers with Parameters<T> and ReturnType<T> to stay in sync automatically.
  • Index the tuple (Parameters<T>[0]) to grab a single parameter type instead of redeclaring it.
  • Be aware that overloaded functions resolve to the last signature; do not assume the first.
  • Prefer deriving types from a single source function over maintaining parallel manual type definitions.
  • Reach for ConstructorParameters/InstanceType when working with classes instead of functions.
Last updated June 29, 2026
Was this helpful?