#typescript #types #advanced

Day 17: Type Guards and Narrowing

Runtime Checks

Welcome to Day 17! We know how to define types. But how do we check them when the code is actually running? This is called Type Narrowing.

Built-in Guards

We’ve already seen typeof.

function padLeft(padding: number | string, input: string) {
  if (typeof padding === "number") {
    return " ".repeat(padding) + input; // padding is number here
  }
  return padding + input; // padding is string here
}

We also have instanceof for classes.

if (x instanceof Date) {
  console.log(x.getTime());
}

Custom Type Predicates (is)

What if we want to check an interface? typeof only checks primitives. We can write a custom function that returns a Type Predicate.

interface Fish { swim: () => void; }
interface Bird { fly: () => void; }

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

The magic is pet is Fish. If the function returns true, TypeScript knows pet is a Fish.

function move(pet: Fish | Bird) {
  if (isFish(pet)) {
    pet.swim(); // OK
  } else {
    pet.fly(); // OK (must be Bird)
  }
}

Challenge for Today

  1. Define interfaces Car (drive) and Boat (sail).
  2. Write a Type Guard function isCar.
  3. Create a function that takes vehicle: Car | Boat.
  4. Use your type guard to call the correct method.

See you on Day 18 for the elegant Discriminated Unions!