Course outline · 0% complete

0/26 lessons0%

Course overview →

Narrowing with typeof and in

lesson 4-2 · ~11 min · 12/26

Proving which member you have

Narrowing is TypeScript following your runtime checks and shrinking a union accordingly. The workhorse is JavaScript's own typeof, which you met in the first JavaScript course:

function double(value: string | number): string | number {
  if (typeof value === "number") {
    return value * 2;      // here value is number
  }
  return value + value;    // here value must be string
}

Inside the if, TypeScript knows value is a number, so arithmetic is allowed. After the if, only string remains, so string concatenation is allowed. No casts, no new syntax. The compiler reads ordinary control flow and updates the type line by line. This is why the if (p.bio !== undefined) check in lesson 3-2 unlocked p.bio.

value: string | numbertypeof value === "number"?true: value is numberfalse: value is string
A typeof check splits the union: each branch of the if sees a smaller, more precise type.

Code exercise · typescript

Run it and follow the two branches: number goes through * 2, string goes through + itself.

Narrowing objects with in

typeof only distinguishes primitives, it answers "object" for every object. To split a union of object shapes, check which property exists with the in operator:

interface Circle { radius: number }
interface Square { side: number }

function area(shape: Circle | Square): number {
  if ("radius" in shape) {
    return 3.14 * shape.radius * shape.radius;
  }
  return shape.side * shape.side;
}

"radius" in shape is plain JavaScript, and TypeScript understands it: inside the if the shape is a Circle, after it a Square. It works, but hunting for distinguishing properties gets fragile as shapes grow. The next lesson shows the pattern real codebases use instead.

Code exercise · typescript

Your turn. Write toLabel(input: string | number): string. If input is a number, return "num:" + input.toFixed(1). Otherwise return "str:" + input.toUpperCase(). Print toLabel(3.14) and toLabel("hi").