ReturnType<Type>

Extracts the return type of a function type, so a value type can be derived instead of duplicated.

Signature
type ReturnType<Type extends (...args: any) => any> = Type extends (...args: any) => infer R ? R : any
  • Added in TypeScript 2.8

Parameters

ParameterTypeDescription
$Type(...args: any) => anyA function type, normally written `typeof someFunction`. For an async function this is the `Promise<T>`, not `T`. Passing a non-function type fails the constraint.

Return value

The declared or inferred return type of the functionThe function's return type. For overloads, the return type of the last signature. `any` if Type is `any`; `never` if Type is `never`.

Errors

A type argument that is not a function type errors with "Type 'X' does not satisfy the constraint '(...args: any) => any'" and falls back to `any` — including the bare `Function` type, which has no call signature.

Changelog

  • PHP 4.5`Awaited<T>` shipped, making `Awaited<ReturnType<typeof asyncFn>>` the standard way to reach the resolved value type of an async function.

ReturnType pulls the return type out of a function type using infer. Its real value is in the direction of dependency: instead of declaring an interface and annotating a function to match it, you write the function and derive the type from it, so the two cannot drift apart.

When to reach for it

  • Inferred object returns. A factory or selector returns an object literal you never named; ReturnType<typeof makeSession> names it.
  • Redux-style stores. type RootState = ReturnType<typeof rootReducer> is the canonical pattern — the state type follows the reducer.
  • Wrappers. Paired with Parameters to give a higher-order function the exact signature of what it wraps.

What trips people up

It needs typeof. ReturnType<createSession> is an error, because createSession is a value. Write ReturnType<typeof createSession>.

Async functions give you the promise. ReturnType<typeof fetchUser> is Promise<User>. Wrap it: Awaited<ReturnType<typeof fetchUser>>.

Overloads resolve to the last signature, silently.

A failed constraint yields any, not never, which is worse — the error is reported at the type argument, but if it is suppressed everything downstream becomes unchecked.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Name the type of an object literal returned by a factory without writing the interface twice.
  • Derive a store's state type from its reducer, as in `type RootState = ReturnType<typeof rootReducer>`.
  • Type the result slot of a cache or wrapper alongside `Parameters` for the arguments.

Frequently asked questions

How do I get the return type of a function in TypeScript?

Use `ReturnType<typeof fn>`. The `typeof` is required because type arguments take types, and a function declaration's name is a value.

Why does ReturnType give me Promise<T> instead of T?

Because an async function's return type genuinely is `Promise<T>`. Wrap it as `Awaited<ReturnType<typeof fn>>`, available since TypeScript 4.5, to unwrap the promise.

Does ReturnType work on overloaded functions?

It resolves to the return type of the last overload signature only. Conditional-type inference picks a single call signature, so earlier overloads are ignored.

More examples

State type derived from a reducer

Prints `2 (inc)` and then `{ count: 2, lastAction: 'inc' }`. `State` is never written out by hand: add a field to the object the reducer returns and every selector typed against `State` sees it immediately, whereas a hand-maintained interface would have silently gone stale.

Example · typescript
function counterReducer(
  state = { count: 0, lastAction: "none" },
  action: { type: "inc" } | { type: "dec" }
) {
  switch (action.type) {
    case "inc": return { count: state.count + 1, lastAction: "inc" };
    case "dec": return { count: state.count - 1, lastAction: "dec" };
  }
}

type State = ReturnType<typeof counterReducer>;

function selectLabel(state: State): string {
  return `${state.count} (${state.lastAction})`;
}

let state: State = counterReducer(undefined, { type: "inc" });
state = counterReducer(state, { type: "inc" });
console.log(selectLabel(state));
console.log(state);

Async returns and the Awaited pairing

Prints `is a promise: true` and then `{ id: 'u1', name: 'Ada', roles: [ 'admin' ] }`. The two aliases show the distinction: `ReturnType` gives back exactly what the function returns, which for an `async` function is always a promise. `Awaited` (TypeScript 4.5+) unwraps it, recursively, and is what you want whenever the type is meant to describe the value the caller ends up holding.

Example · typescript
async function fetchUser(id: string) {
  return { id, name: "Ada", roles: ["admin"] };
}

type Raw = ReturnType<typeof fetchUser>;          // Promise<{...}>
type User = Awaited<ReturnType<typeof fetchUser>>; // the resolved object

const pending: Raw = fetchUser("u1");
console.log("is a promise:", pending instanceof Promise);

const user: User = await pending;
console.log(user);

// const wrong: User = fetchUser("u2");
//   -> Type 'Promise<...>' is not assignable to type '{ id: string; ... }'

Discussion

  • Be the first to comment on this lesson.