TypeScript: Type Guards

 by Robin Wieruch
 - Edit this Post

Type Guards in TypeScript are needed whenever you want to allow only a certain type for a routine in TypeScript. In this TypeScript tutorial, you will learn how to check for user defined types by using so-called user-defined type guards.

We will start with two types and two objects annotated respectively with these types:

type Dog = {
name: string;
age: number;
};
const trixi: Dog = {
name: 'Trixi',
age: 49, // dog years
};
type Person = {
firstName: string;
lastName: string;
age: number;
};
const robin: Person = {
firstName: 'Robin',
lastName: 'Wieruch',
age: 7,
};

In this TypeScript example, we will get to our use case for type guards by declaring a function which increases the age of each type equally:

const celebrateBirthday = (mammal: Person | Dog) => {
return {
...mammal,
age: mammal.age + 1,
};
};
console.log(celebrateBirthday(trixi).age);
// 50
console.log(celebrateBirthday(robin).age);
// 8

However, earlier when we defined the TypeScript annotated dog object, you already saw that the Dog type uses the age property differently compared to the Person type.

Problem: In the previous example, the dog is 49 years old in dog years and 7 years old in human years. So naturally when celebrating a dog's birthday, the age should not increase by 1 but by 7 when we sticking to expressing the age in dog years.

User-Defined Type Guard

We will use a user-define type guard to conditionally implement logic. While the function should increase the age by one for a Person type, the should increase it by seven for a Dog type. In other words, based on the argument's type of function, it should conditionally apply logic.

const isDog = (mammal: Person | Dog): mammal is Dog => {
return (mammal as Dog).name !== undefined;
};

This new function checks for a user defined type now. While we allow passing in a Person or Dog type as object to this function, the function says that it must return an object of type Dog by using a forced type assertion (also called type casting).

Within this function, to avoid raising any TypeScript errors, we cast the function's parameter to a Dog type and check that the name property is not undefined. Because after all, only objects of type Dog have a name property whereas objects of type Person have a firstName and lastName property.

Next, use this user defined type guard as a function call for the actual domain logic:

const celebrateBirthday = (mammal: Person | Dog) => {
return {
...mammal,
age: isDog(mammal) ? mammal.age + 7 : mammal.age + 1,
};
};
console.log(celebrateBirthday(trixi).age);
// 56
console.log(celebrateBirthday(robin).age);
// 8

And it works. If the argument to this function is of type Dog, its age property gets increased by seven. If it's not of type Dog, then it only increases by one.

Continue Reading:

Last but not least, we want to avoid repetitive type declarations, so we can extract these as union type and reuse this new union type at multiple places:

type Mammal = Person | Dog;
const isDog = (mammal: Mammal): mammal is Dog => {
return (mammal as Dog).name !== undefined;
};
const celebrateBirthday = (mammal: Mammal) => {
return {
...mammal,
age: isDog(mammal) ? mammal.age + 7 : mammal.age + 1,
};
};

Essentially that's the gist to guard against a a user defined type.

Bonus: If you only want to guard against a JavaScript primitive (e.g. number), just checking for typeof (e.g. typeof mammal.age === 'number') is sufficient though. If the subject you want to guard against is a JavaScript class (e.g. class Dog { ... }), you can use instanceof (e.g. dog instanceof Dog) instead.

Keep reading about 

When using React's useState Hook in TypeScript, the method usually infers the implicit type for the returned state from the provided argument automatically. In the following example, React's useState…

When using function components in React, we may want to type their props with TypeScript. Overall there are two ways of making a React component type safe with TypeScript, however, let's start by…

The Road to React

Learn React by building real world applications. No setup configuration. No tooling. Plain React in 200+ pages of learning material. Learn React like 50.000+ readers.

Get it on Amazon.