Awaited<Type>

Unwraps a type the way await does, recursively resolving nested promises and thenables.

Signature
type Awaited<T> = T extends null | undefined ? T : T extends object & { then(onfulfilled: infer F, ...args: infer _): any } ? (F extends ((value: infer V, ...args: infer _) => any) ? Awaited<V> : never) : T;
  • Added in TypeScript 4.5

Parameters

ParameterTypeDescription
$Tany typeThe type to unwrap. Usually a `Promise<...>` or the return type of an async function, but any type is accepted.

Return value

The fully unwrapped value typeThe type `await` would produce for a value of `T`: promises and thenables unwrapped recursively, everything else returned unchanged.

Errors

Nothing at the type level, but a thenable whose `then` callback is not a function type resolves to `never`, which usually surfaces later as an unassignable value.

Changelog

  • PHP 4.5Introduced alongside improved inference for `Promise`, `await`, and `Promise.all`, whose signatures in the lib now use it.

Awaited answers one question: what do I actually get when I await this? An async function that returns Promise<T> gives you T; one that returns Promise<Promise<T>> still gives you T, because await keeps unwrapping until the value is not a thenable. The type mirrors that behaviour exactly.

Reach for it whenever you need to name the resolved value of something you did not declare yourself. The canonical form is Awaited<ReturnType<typeof loader>>, which extracts the data type out of an async function without exporting a separate interface, and survives refactors of the function body. Framework users hit this constantly: the payload type of a route loader, a query function, or a generated API client is often only discoverable through its return type.

Why not just write your own

The naive version, T extends Promise<infer U> ? U : T, unwraps exactly one level and knows nothing about non-Promise thenables. Awaited recurses, handles unions by distributing over each member, and passes null and undefined straight through. That is why Promise.all and the type of the await operator itself are defined in terms of it in the standard library.

On a plain, non-promise type it is a no-op, so wrapping a value that might or might not be async is always safe.

Example

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

When to use it

  • Name the payload type of an async loader with `Awaited<ReturnType<typeof load>>`.
  • Type a cache that stores resolved values keyed by the promises that produced them.
  • Write a helper generic over `T` that works with both a value and a promise of that value.

Frequently asked questions

How do I get the type a promise resolves to?

Write `Awaited<typeof myPromise>`, or `Awaited<ReturnType<typeof myAsyncFn>>` for a function. It unwraps every nested layer, so `Promise<Promise<string>>` resolves to `string` rather than `Promise<string>`.

What is the difference between Awaited<T> and T extends Promise<infer U> ? U : T?

The hand-rolled conditional peels exactly one layer and only recognises the `Promise` type itself. `Awaited` recurses until the type is no longer thenable and matches any object with a compatible `then` method, which is why the standard library uses it for `await` and `Promise.all`.

Does Awaited do anything to a non-promise type?

No — it returns the type unchanged, and `null` and `undefined` pass straight through. That makes it safe to apply to a generic parameter that may or may not be a promise.

More examples

Extracting the data type out of an API client

No `Order` interface is ever declared by hand — the shape is read straight out of the async method, so adding a field to the returned object immediately makes it available on `Order`. The log prints `A-1001: 2 items, 24.50`.

Example · typescript
const api = {
  async getOrder(id: string) {
    return { id, total: 24.5, items: ["mug", "beans"] };
  },
};

type Order = Awaited<ReturnType<typeof api.getOrder>>;

function summarize(order: Order): string {
  return `${order.id}: ${order.items.length} items, ${order.total.toFixed(2)}`;
}

const order: Order = await api.getOrder("A-1001");
console.log(summarize(order));

Writing a helper that accepts a value or a promise

`Awaited<T>` is what makes the return type honest: if a caller passes an already-promised value, the result is not `Promise<Promise<X>>` but the flat resolved type. Output is `1 two [ true, 4 ]` followed by `number string` — the same code path handles both sync and async inputs.

Example · typescript
type MaybePromise<T> = T | Promise<T>;

async function normalize<T>(input: MaybePromise<T>): Promise<Awaited<T>> {
  return (await input) as Awaited<T>;
}

const a = await normalize(1);
const b = await normalize(Promise.resolve("two"));
const all = await Promise.all([normalize(true), normalize(Promise.resolve(4))]);

console.log(a, b, all);
console.log(typeof a, typeof b);

Discussion

  • Be the first to comment on this lesson.