Suggest an editImprove this articleRefine the answer for “Utility type awaited in TypeScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Awaited<T>** is a TypeScript utility type that recursively unwraps Promise chains to return the final resolved value type. ```typescript type A = Awaited<Promise<string>>; // string type B = Awaited<Promise<Promise<number>>>; // number ``` **Key point:** use `Awaited<ReturnType<typeof fn>>` to type async function results precisely.Shown above the full answer for quick recall.Answer (EN)Image**Awaited<T>** is a TypeScript utility type, available since v4.5, that recursively unwraps Promise chains and returns the final resolved value type. ## Theory ### TL;DR - Analogy: each Promise is a wrapper box. Awaited opens every box, no matter how many layers deep, and gives you what is inside. - Core behavior: unwraps single Promises, nested Promises, and union types. Returns T unchanged if T is not a Promise. - Decision rule: use `Awaited<ReturnType<typeof fn>>` to type async function results; combine with `ReturnType` for the full pattern. - Available since TypeScript 4.5. ### Quick example ```typescript // Single layer type A = Awaited<Promise<string>>; // string // Nested - TypeScript recurses automatically type B = Awaited<Promise<Promise<number>>>; // number // Union - distributes over each member type C = Awaited<Promise<string> | number>; // string | number // Practical: async function return type async function fetchUser(): Promise<{ id: number }> { return { id: 42 }; } type UserType = Awaited<ReturnType<typeof fetchUser>>; // { id: number } ``` TypeScript strips each Promise layer one by one until it reaches a plain type. The union case is worth noting: `Awaited<Promise<A> | B>` becomes `A | B`, not `Promise<A | B>`. ### How recursive unwrapping works TypeScript implements `Awaited` as a conditional type: if `T extends Promise<infer U>`, recurse on `Awaited<U>`; else return `T`. This happens entirely at compile time. No runtime object, no overhead. V8 and Node never see it. Union distribution is built in. `Awaited<Promise<A> | Promise<B>>` resolves to `A | B` because TypeScript maps the conditional over each union member separately. That makes it safe to use with API functions that return `Promise<ResponseA> | Promise<ResponseB>` depending on a parameter. I have seen this trip up developers working with older SDK wrappers that return `Promise<Promise<T>>` due to double-wrapping. Awaited handles it without any extra work. ### When to use - **Async function returns:** `Awaited<ReturnType<typeof fn>>` gives you the resolved type without manual assertions. - **API chains:** when Promise depth is unknown or varies across SDK versions. - **Union of async results:** `Awaited<Promise<string> | Promise<number>>` collapses to `string | number`. - **`Promise.all` typing:** pair with a mapped type over a tuple to unwrap each element. Skip it when T is already a plain type. `Awaited<string>` just returns `string`. Correct, but pointless. ### Common mistakes **Applying Awaited to a function type instead of its return type:** ```typescript type Fn = () => Promise<number>; type Wrong = Awaited<Fn>; // never type Right = Awaited<ReturnType<Fn>>; // number ``` A function type does not extend `Promise<infer U>`, so Awaited falls through to `never`. Always wrap with `ReturnType` first. **Manual single-layer unwrapping that fails on nested Promises:** ```typescript // Old polyfill pattern (pre-4.5) type OldWay<T> = T extends Promise<infer U> ? U : T; type A = OldWay<Promise<Promise<string>>>; // Promise<string> - still wrapped type B = Awaited<Promise<Promise<string>>>; // string - correct ``` The manual version does not recurse. If you see this in a legacy codebase, replace it with `Awaited`. **Forgetting ReturnType when working with async functions:** ```typescript async function loadData() { return { id: 1, name: "Alice" }; } type T1 = ReturnType<typeof loadData>; // Promise<{ id: number; name: string }> type T2 = Awaited<ReturnType<typeof loadData>>; // { id: number; name: string } ``` **Expecting Awaited to unwrap Promise fields inside objects:** ```typescript type Obj = { data: Promise<string> }; type T = Awaited<Obj>; // Obj - unchanged ``` Awaited only touches the top-level Promise. It does not recurse into object shapes. You need a recursive mapped type for that. ### Real-world usage - **TanStack Query:** `type Data = Awaited<ReturnType<typeof fetchUser>>` for typed hook data without manual assertions. - **tRPC:** `inferRouterOutputs<AppRouter>` uses Awaited internally to type all procedure results. - **Express async handlers:** `Awaited<ReturnType<typeof handler>>` for middleware return type inference. - **Zod with async transforms:** when a schema has an async transform step, Awaited resolves the output type correctly. ### Follow-up questions **Q:** What does `Awaited<Promise<void>>` resolve to? **A:** `void`. TypeScript does not collapse `void` to `never` here. That is the correct behavior for fire-and-forget functions. Awaiting them gives you `void`, and Awaited reflects that. **Q:** How does Awaited distribute over union types? **A:** `Awaited<Promise<A> | B>` becomes `Awaited<Promise<A>> | Awaited<B>`, which is `A | B`. TypeScript maps the conditional over each union member separately. **Q:** What is the difference between Awaited and the old manual unwrap pattern? **A:** The manual pattern `T extends Promise<infer U> ? U : T` unwraps exactly one layer. Awaited recurses until it hits a non-Promise type and also handles union distribution correctly. **Q:** Implement Awaited manually without the built-in type. **A:** ```typescript type MyAwaited<T> = T extends Promise<infer U> ? MyAwaited<U> : T; ``` This covers the recursion. TypeScript handles union distribution automatically for distributive conditional types. **Q:** In React Query, why use `Awaited<ReturnType<typeof fn>>` instead of typing `data` manually? **A:** Because it tracks the `queryFn` signature automatically. Change the function return type and the hook data type updates with it. A manual annotation can go stale without any compiler warning. ## Examples ### Basic: extracting a resolved type from an async function ```typescript interface User { id: number; name: string; } async function fetchUser(id: number): Promise<User> { const res = await fetch(`/api/users/${id}`); return res.json(); } // ReturnType alone - still wrapped in Promise type Wrapped = ReturnType<typeof fetchUser>; // Promise<User> // Add Awaited - Promise layer removed type Resolved = Awaited<ReturnType<typeof fetchUser>>; // User ``` `ReturnType` extracts whatever the function returns, Promise included. `Awaited` strips the wrapper. The combination is the standard pattern for typing async results in production code. ### Typing a React Query hook ```typescript interface Post { id: number; title: string; } async function fetchPost(id: number): Promise<Post> { const res = await fetch(`/api/posts/${id}`); return res.json(); } type PostData = Awaited<ReturnType<typeof fetchPost>>; // Post function usePost(id: number) { return useQuery({ queryKey: ["post", id], queryFn: () => fetchPost(id), // data is inferred as Post | undefined automatically }); } ``` TanStack Query v5 infers the `data` type from `queryFn` using Awaited internally. You get `Post | undefined` on `data` without writing a single type annotation on the hook itself. ### Edge case: union with void ```typescript type Complex = Promise<Promise<string> | void>; type Resolved = Awaited<Complex>; // Step 1: Awaited<Promise<string> | void> // Step 2: Awaited<Promise<string>> | Awaited<void> // Step 3: string | void ``` `Awaited<void>` stays `void`, not `never`. If you need to remove `void` from the result, use `Exclude<Resolved, void>`, which gives `string`. Those are two separate operations. Awaited does not do both.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.