BeCuriouzLearn something new every day
Typing Async Code: What await Unwraps
💡 async wraps your value in Promise<T>; await (and Awaited<T>) recursively unwraps it back to T.
An async function never returns a plain value — it always returns a Promise, where T is what the function eventually resolves to. TypeScript enforces this automatically: if the body returns a number, the function's type is Promise, even if you wrote : number by mistake (that's a type error). You annotate the resolved type and let the compiler wrap it:
async function getPrice(): Promise<number> {
return 42; // fine — TS wraps 42 in a Promise
}The await keyword does the reverse: it unwraps a Promise back into a T. So const p = getPrice() has type Promise, but const n = await getPrice() has type number. A key subtlety is that await unwraps recursively — Promises don't nest. await on a Promise still gives you a string, mirroring the runtime, where returning a promise from an async function auto-flattens it.
To name the unwrapped type without calling the function, use the built-in Awaited utility. Awaited is number, and because it recurses, Awaited is boolean. This is exactly the type you get from await, which is handy when combining promises: await Promise.all([getPrice(), getName()]) infers a tuple [number, string].
Remember from earlier cards that union and inference rules still apply here — an async function that might return a string or number is typed Promise, and awaiting it gives string | number, which you'll often need to narrow before use.