Typing Promises & async/await
Advanced · 20 min read · ▶ live playground · ✦ checkpoint
Type a promise with Promise<T>: T is the value you get after the async work succeeds. An async function always returns a promise, so forgetting await leaves you holding the not-yet value instead of the data.
Section 14 starts where the earlier async glance stopped. You already know the boundary rule: unknown data must be checked before it becomes trusted data. Now you add the async layer without losing those types.
Promise<T> means the future value is T
Promise<T> is a generic type for a not-yet value whose successful result has type T. A promise is fulfilled when the async work succeeds and produces that value.
await pauses inside async code until a promise fulfills, then gives you the fulfilled value. An async function is a function marked with async; it always gives callers a Promise, even when the body returns a plain value.
→ ada finished 42 lessons
Read the types out loud. loadProfile returns a not-yet Profile. profileLine returns a not-yet string. Inside profileLine, await loadProfile(handle) changes the not-yet Profile into the real Profile for that line of code.
The commented line fails because a Promise<Profile> is not a Profile. The type parameter tells you what comes out later; it doesn't make the wrapper disappear now.
Awaited<T> names the after-await type
Awaited<T> is a utility type that gives you the type you would have after awaiting T. You use it when the value type already lives behind a function type, a wrapper, or a helper.
That matters in real code because you don't want to write the same result shape twice. The function is the source of truth; the type reads from it.
→ mina has 910 async points
ReturnType<typeof loadScore> is Promise<{ handle: string; points: number }>; Awaited unwraps the not-yet layer and leaves the score object. It also unwraps nested promises, which is why it is better than a one-off homegrown helper.
The Promise combinators keep useful types
A Promise combinator is a built-in method that coordinates several promises. The common three are Promise.all, Promise.allSettled, and Promise.race.
Promise.allwaits for every promise and preserves the array positions like tuple seats.Promise.allSettledwaits for every promise and gives each result astatusdiscriminant.Promise.racesettles as soon as one promise wins; afterawait, mixed fulfilled values produce a union.
→ nina: 12/14
→ ok: nina badge ready
→ not ok
→ race string: CACHED PROFILE
Promise.all knows the first seat is Profile and the second seat is Progress, so destructuring keeps both names precise. allSettled gives you a discriminated union: check status before reading value. race can't promise which candidate wins, so first is string | number and needs normal narrowing.
The floating promise trap
The checker catches many promise/value mix-ups, but it does not reject every forgotten await. Starting async work and moving on is legal JavaScript.
→ right now: 0
→ after a turn: 1
The small rule is blunt: if later code depends on async work, await it before that later code runs. If you intentionally start background work, make that intent visible in the surrounding code instead of letting the promise drift away.
Next lesson moves the same ideas to fetch: real network data arrives asynchronously and still crosses the runtime boundary as unknown until you validate it. We'll keep the live playground simulated there too, so the checker can prove the typed core without depending on a network call.
Checkpoint
Answer all three to mark this lesson complete