TypeScript - Beware the user-defined type guards

This post is part of an ongoing series on TypeScript. In it, I try to explain in a detailed way different situations I’ve faced while building relatively big React applications using TypeScript, and moving them from JavaScript. Here’s a list of other posts I’ve written about TypeScript:


The is keyword in TypeScript is used to specify that a function is a type predicate. Here’s the Wikipedia entry for predicates:

In mathematical logic, a predicate is commonly understood to be a Boolean-valued function P: X → {true, false}, called the predicate on X. […] Informally, a predicate is a statement that may be true or false depending on the values of its variables.

A type predicate is a function returning a boolean value that can be used to identify the type of an expression. In TypeScript, type predicates can be expressed using the following syntax:

function isCollie(dog: Dog): dog is Collie {
  return dog.breed === 'collie' // Must be a Collie
}

Type guards can be implemented using the instanceof and typeof JavaScript operators, but can also be implemented with the is keyword like in the example above. Type guards of this nature are user-defined type guards.

While useful, user-defined type guards are a vector for runtime errors in TypeScript applications. It’s very easy to implement them in a way that makes a piece of code unsafe. Unsuspecting developers that use them from there on may expand the surface of unsafety. Here’s some example code:

interface Animal {}

interface Bird extends Animal {
  fly: Function
}

interface Dog extends Animal {
  bark: Function
}

function isBird(animal: Animal): animal is Bird {
  return true // If you say so...
}

const norbert: Dog = { bark: () => console.log('Wharg!') }

if (isBird(norbert)) {
  norbert.fly() // Compiles, error during runtime
}

Granted, that’s a sloppy snippet, but it should serve to illustrate the example. Out in the wild, there are some things you can try to keep in mind in order to avoid this type of problem:


Related documentation and links: