Course outline · 0% complete

0/26 lessons0%

Course overview →

null, undefined, and Optional Access

lesson 4-4 · ~11 min · 14/26

The missing-value unions

The most common JavaScript crash is reading a property of a value that is not there: Cannot read properties of undefined. Every real program has values that can be missing — a search with no match, an optional field never filled in, a setting the user skipped. TypeScript's answer is to put the absence into the type, then apply this unit's narrowing discipline to it.

JavaScript has two absence values: undefined (what you get when nothing was assigned, such as a missing property) and null (a value code assigns on purpose to mean "empty"). TypeScript treats each as its own type, so a function that can fail says so in its signature:

function firstStartingWith(names: string[], letter: string): string | null {
  for (const n of names) {
    if (n.startsWith(letter)) {
      return n;
    }
  }
  return null;
}

The return type is honest: usually a string, sometimes null. Callers cannot call string methods on the result until a check like !== null narrows the null away — the union rule from lesson 4-1, applied to absence. This one convention eliminates the whole class of "forgot the no-match case" bugs, because forgetting is now a compile error.

Code exercise · typescript

Run it. The first caller narrows before use; the last line prints the raw result, so a missed search prints null. Then try calling .toUpperCase() directly on that last result and run again — the compiler blocks it until you narrow.

?. and ??: the built-in shortcuts

Narrowing with if works everywhere but gets wordy when absence is routine. JavaScript ships two operators for exactly this, and TypeScript understands both fully.

Optional chaining a?.b reads the property only when a is not null or undefined; otherwise the whole expression evaluates to undefined instead of crashing. The chain stops at the first missing link, so p.team?.city has type string | undefined.

Nullish coalescing x ?? fallback produces x unless it is null or undefined, in which case it produces the fallback. It exists because the older || trick also replaces perfectly valid values — 0, "", and false are all "falsy", so count || 1 silently turns a legitimate 0 into 1. ?? replaces only true absence.

const city = p.team?.city ?? "no city";

Read it: take the team's city if there is a team, otherwise use "no city". The result is a plain string — no union left, because the fallback closed the undefined hole. The pair ?. + ?? is the everyday idiom for optional data in production TypeScript.

Code exercise · typescript

Run both calls. Sam has no team, so p.team?.city evaluates to undefined and ?? supplies the fallback. Then replace ?? with || and change Mia's city to the empty string "" to see why ?? is the safer default.

Quiz

What is the difference between value ?? fallback and value || fallback?

Code exercise · typescript

Your turn. Given interface Order { id: number; note?: string }, write noteLength(o: Order): number that returns the note's length, or 0 when there is no note — one line, using ?. and ??. Print the two calls shown in the starter.