TYPESCRIPT / CHAPTER 11

Typed asynchronous results and explicit failure

Advanced

Model failure without confusing it with a missing return value.

By the end of this chapter

  • Read Promise result types
  • Narrow fulfilled domain-result unions
  • Distinguish rejection from explicit error variants

The idea

Promise<T> describes the value obtained when a promise fulfills; it does not describe every possible rejection. An async function returning a number produces Promise<number>. Await extracts the fulfilled value but can throw on rejection, so callers still need a failure policy. Type annotations alone cannot prove a network operation succeeds.

Expected domain failures can instead be represented by a discriminated result inside the promise. The success variant contains data, while the error variant carries an explanation. Awaiting that promise gives the union, which must be narrowed before reading success-only fields. This does not eliminate unexpected exceptions: decide which errors become domain results and which propagate. Keep ordinary absence distinct from an accidentally omitted return.

TypeScript example
type Lookup = {ok: true; value: number} | {ok: false; reason: string};
async function lookup(found: boolean): Promise<Lookup> {
  return found ? {ok: true, value: 6} : {ok: false, reason: "missing"};
}
async function main() {
  const result = await lookup(false);
  console.log(result.ok ? result.value : result.reason);
}
main();
Result
missing

After await, the discriminant controls which field is safe to read.

Try it yourself

Write a typed asynchronous parser that returns a failure variant for invalid input while allowing unexpected I/O failures to reject.

Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.

CHECK YOUR UNDERSTANDING

Chapter checkpoint

What is the declared type of the value produced by awaiting Promise<number>?