Type Guard is simply a technique of checking if a certain property or method exists before we try to use it. To understand why we might need this feature in TypeScript, let’s look at an example,

type Message = string;

function display(message: Message) {
  console.log(message.toLowerCase());
}

display("HELLO"); // hello

In the above example, we have a function display() that takes a single parameter of type Message. The type of Message is a string for now.

Let’s say, the message can be of type string or a number.

type Message = string | number;

function display(message: Message) {
  console.log(message.toLowerCase()); // ❌ Property 'toLowerCase' does not exist on type 'Message'.
  // ❌ Property 'toLowerCase' does not exist on type 'number'.
}

As you can see, TypeScript throws an error, Property 'toLowerCase' does not exist on type 'Message'.Property 'toLowerCase' does not exist on type 'number'.

This is because we are trying to use a method toLowerCase() which works if the message is string but it will not work if we pass a number. Since the value of the parameter message is determined at runtime we need a way to implement a type guard. Let’s look at different ways in which we can solve such problems in TypeScript.

Using typeOf

typeOf is a Javascript operator used for type checking. We can implement type guards in Typescript using typeOf. Let’s rewrite the same example that we have looked at before using typeOf.

type Message = string | number;

function display(message: Message) {
  if (typeof message === "string") {
    console.log(message.toLowerCase());
  } else {
    console.log(message);
  }
}

display("HELLO"); // hello
display(1234); // 1234

As you can see from the above example, using typeOf operator we ensure the toLowerCase() code only runs if we pass a string. Otherwise, we just display the number as it is.

Using ‘in’ operator

The typeof operator comes in handy if we are checking for primitive types. But it will not work for more advanced types like for example a custom type like the one below.

type Dog = {
  fetch: boolean,
};

type Cat = {
  sleep: boolean,
};

type Animal = Dog | Cat;

function checkFetch(animal: Animal) {
  console.log(`Can Fetch? - ${animal.fetch}`); // ❌ Property 'fetch' does not exist on type 'Animal'.
  // ❌  Property 'fetch' does not exist on type 'Cat'.
}

In the above example, we have Animal type which is a union type of Dog and Cat. We also have a checkFetch that displays if an animal passed to it can fetch.

TypeScript cannot determine what type of parameter can get passed to this function, hence, it will complain, Property 'fetch' does not exist on type 'Animal'.Property 'fetch' does not exist on type 'Cat'.

We can use the in operator available in Javascript to check if a property exists. In this case, to check if fetch property exists.

type Dog = {
  fetch: boolean,
};

type Cat = {
  sleep: boolean,
};

type Animal = Dog | Cat;

function checkFetch(animal: Animal) {
  if ("fetch" in animal) {
    console.log(`Can Fetch? - ${animal.fetch}`);
  } else {
    console.log("Sorry cannot fetch");
  }
}

let dog: Dog = {
  fetch: true,
};

let cat: Cat = {
  sleep: true,
};

checkFetch(dog); // Can Fetch? - true
checkFetch(cat); // Sorry cannot fetch

Using instanceOf

instanceOf is a Javascript operator that is used to check the type of an object. This operator is helpful when you want to check if an object belongs to a particular class.

class Dog {
  fetch() {
    console.log("I love to fetch");
  }
}

class Cat {
  sleep() {
    console.log("I love to sleep");
  }
}

type Animal = Dog | Cat;

function checkFetch(animal: Animal) {
  if (animal instanceof Dog) {
    animal.fetch();
  } else {
    console.log("Sorry cannot fetch");
  }
}

let dog = new Dog();
let cat = new Cat();

checkFetch(dog); // I love to fetch
checkFetch(cat); // Sorry cannot fetch

In this example, we execute animal.fetch() code only if animal is an instance of class Dog.