Exhaustiveness checking in TypeScript

René Kulik on 09.11.2022

When you are programming in TypeScript you might use switch statements whose cases are based on union types, e.g.:

type Fruit = 'banana' | 'apple';

const getColor = (fruit: Fruit) => {
  switch (fruit) {
    case 'banana':
      return 'yellow';
    case 'apple':
      return 'red';
  }
};

console.log(getColor('banana'));

In this example we have a union type Fruit which consists of two strings, “banana” and “apple”. Furthermore we have a function called getColor which returns the color of a given fruit. In this case, as we pass in “banana”, we are logging “yellow”.

The code is valid and it works flawlessly. But what happens if Fruit gets extended?

type Fruit = 'banana' | 'apple' | 'pear';
...

The code still works but we won’t get any color if we pass in “pear”, the switch statement does not handle it. It would be beneficial if TypeScript would notify us that we have an unhandled case. Fortunately, this is possible by using a technique called exhaustiveness checking. Therefore, we first extend the switch statement by adding a default case and create a variable from type never which we assign our fruit parameter to:

type Fruit = 'banana' | 'apple' | 'pear';

const getColor = (fruit: Fruit) => {
  switch (fruit) {
    case 'banana':
      return 'yellow';
    case 'apple':
      return 'red';
    default:
      const _exhaustiveCheck: never = fruit;
      return _exhaustiveCheck;
  }
};

console.log(getColor('banana'));

As no type is assignable to never, this will result in the following error and we won’t be able to compile our code: Type 'string' is not assignable to type 'never'.

To fix this, we have to handle all fruits properly:

type Fruit = 'banana' | 'apple' | 'pear';

const getColor = (fruit: Fruit) => {
  switch (fruit) {
    case 'banana':
      return 'yellow';
    case 'apple':
      return 'red';
    case 'pear':
      return 'green';
    default:
      const _exhaustiveCheck: never = fruit;
      return _exhaustiveCheck;
  }
};

console.log(getColor('banana'));