TypeScript: Exhaustive Pattern Matching on Classes with instanceof

Essay - Published: 2025.12.24 | create | types | typescript |

DISCLOSURE: If you buy through affiliate links, I may earn a small commission. (disclosures)

TS has pretty good types and can do exhaustive pattern matching. Here's an example of doing so with classes and instanceof - smth I didn't think was possible (and neither did my AI).

See the full code and run it - TS Playground.

What's exhaustive pattern matching?

Exhaustive pattern matching is when the type system can tell if there are unhandled cases. This is useful to catch bugs at compile time instead of runtime.

In TypeScript, we can do exhaustive pattern matching in a few ways but a common one is to use the never type to declare a variable as a type that should never be hit. This way the type system will raise errors if that type could be hit during runtime (at least according to the types).

Narrowing Classes with instanceof

We can narrow on possible classes by using instanceof.

class Dog {
  speak() {
    console.log("bark");
  }
}

class Cat {
  speak() {
    console.log("Mewo");
  }
}

class Snake {
  speak() {
    console.log("Hiss");
  }
}

function matching(input: Dog | Cat | Snake): void {
  if(input instanceof Dog) {
    input.speak()
    return;
  } else if(input instanceof Cat) {
    input.speak()
    return;
  }

  const exhaustive: never = input;
  console.log(`Should not get here! Got ${JSON.stringify(exhaustive)}`);
}

Narrowing classes and types

We can combine these with lean type discriminators as well. Note that I had to narrow out the classes in order to reach the discriminators as before that the type system doesn't know if kind should exist on the input or not.

type NotAClass1 = {
  kind: "NotAClass1", 
  a: string;
}

type NotAClass2 = {
  kind: "NotAClass2", 
  a: string;
}

function matchingWithNoClass(input: Dog | Cat | Snake | NotAClass1 | NotAClass2): void {
  if (input instanceof Dog) {
    input.speak();
    return;
  } else if (input instanceof Cat) {
    input.speak();
    return;
  } else if (input instanceof Snake) {
    input.speak();
    return;
  } else if (input.kind === "NotAClass1") {
    console.log(`NotAClass1: ${input.a}`);
    return;
  }
  // } else if (input.kind === "NotAClass2") {
  //   console.log(`NotAClass2: ${input.a}`);
  //   return;
  // }

  const exhaustive: never = input;
  console.log(`Should not get here! Got ${JSON.stringify(exhaustive)}`);
}

Next

So now you know - you can discriminate classes ergonomically in TS.

If you liked this post, you might also like:

Want more like this?

The best way to support my work is to like / comment / share for the algorithm and subscribe for future updates.