C.W.K.
Stream
Lesson 02 of 05 · published

`async` / `await` Typing

~8 min · async-promises, async-await, syntax

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"async wraps the return; await unwraps the Promise. Two halves of the same operation."

The two halves

async function declares a function whose return is always wrapped in a Promise. await expression inside an async function pauses until the Promise resolves, then resumes with the resolved value. The result: async code reads like synchronous code, but the type system tracks the Promise wrapper across calls.

Implicit return wrapping

Returning a plain value from an async function: TypeScript automatically wraps it in a Promise. async function f(): Promise<number> { return 42 } — the return 42 becomes Promise<number>.

Returning a Promise from an async function: TypeScript flattens it. async function g(): Promise<User> { return fetchUser(1) /* returns Promise<User> */ } — the result is still Promise<User>, not Promise<Promise<User>>. Promises don't nest.

Await on non-Promises

await value where value isn't a Promise just resolves to the value. The runtime wraps it in Promise.resolve internally. This means await 42 is 42. Useful occasionally; rarely written explicitly.

The mental model: async functions return a Promise; await peeks inside that Promise. Every async expression has a Promise type at one level and the unwrapped type one level deeper. The wrapper is what makes the type checker know what's happening.

Code

async wraps, await unwraps·typescript
// async wraps the return in Promise<T>.
async function getNumber(): Promise<number> {
  return 42;                          // wrapped → Promise<number>
}

// Returning a Promise — flattened.
async function getUser(): Promise<User> {
  return fetchUser(1);                // returns Promise<User>, NOT Promise<Promise<User>>
}

// await — unwraps in async functions only.
async function main() {
  const n = await getNumber();        // n: number
  const u = await getUser();          // u: User

  // await on non-Promise — just returns the value.
  const x = await 42;                  // x: number
}

// Top-level await — works in ESM but not CommonJS by default.
const data = await fetch('/api/data');  // valid in modern ESM modules

External links

Exercise

Write an async function loadProfile(id: number): Promise<Profile> that awaits a user, awaits their posts, and returns a Profile combining both. Confirm each intermediate value has the unwrapped type via hover.
Hint
Each await fetchUser(id) unwraps Promise<User> to User. The combining is just a plain object literal returned from the async function — TypeScript wraps it back into Promise<Profile> automatically.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.