Write TypeScript like a pro. Typescript Icon

Follow the ultimate TypeScript roadmap.

User defined Type Guards in TypeScript

Welcome back to the TypeScript Type Guards series! You’re entering the realms of next level knowledge, woohoo!

For reference, the 4 articles in this TypeScript series:

Enjoying the series? Come and master the whole TypeScript language with us across 2 courses, beginner concepts and finally advanced. Full of real-world examples, it’ll answer so many questions you have and will have. Click here to check out more of what you’ll learn with us!

Table of contents

User Defined Type Guards mean that the user can declare a particular Type Guard or you can help TypeScript to infer a type when you use, for example, a function.

We will demonstrate this with the following code:

class Song {
  constructor(public title: string, public duration: number) {}
}

class Playlist {
  constructor(public name: string, public songs: Song[]) {}
}

function getItemName(item: Song | Playlist) {
  if(item instanceof Song) {
    return item.title;
  }
  return item.name;
}

const songName = getItemName(new Song('Wonderful Wonderful', 300000));
console.log('Song name:', songName);

const playlistName = getItemName(
new Playlist('The Best Songs', [new Song('The Man', 300000)])
);
console.log('Playlist name:', playlistName);

The instanceof and typeof operators aren’t always the best tool to use. In a scenario where you may want to create or break something down into a reusable function, we lose the type inference if we do not actually have it in our item instanceof Song because TypeScript cannot infer the type further down. There is a way around this.

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

What we are going to do is create a function and we’ll say this is a Song. If it is a Song return me the title, otherwise return me the name.

function getItemName(item: Song | Playlist) {
  if(isSong(item)) {
    return item.title;
  }
  return item.name;
}

If we were to do this we would be getting some errors from TypeScript. One red underline would be under title and the other under name. Let’s go an create the function isSong. We will pass the argument item of type any. We are not going to change the behaviour but we are going to say is the item is an instanceof Song.

function isSong(item: any) {
  return item instanceof Song;
}

This code would completely work at runtime however at the compile time with TypeScript we might get a boolean back from our getItemName function on isSong , which is the inferred type because instanceof will return true or false. However we don’t get any type information.

Instead of doing something like this:

function getItemName(item: Song | Playlist) {
  if(isSong(item)) {
    return (item as Song).title;
  }
  return item.name;
}

We want to let our Union type in the function argument instruct TypeScript of what we are dealing with. This is the crucial part for understanding a user defined type guard.

So in our isSong function we can provide extra information. We can say that item is a Song. That way when this function evaluates to true we are manually defining and we are returning type information to say that if this function successfully returns true then what we are dealing with is a Song.

function isSong(item: any): item is Song {
  return item instanceof Song;
}

This is what we call a User Defined Type Guard, you can create any kind of type, any kind of check but it’s up to you to make sure that the item instanceof song, in this case, returns a boolean. If we were to return an object instead we would see an error because we can only use the is syntax when we are testing something like a boolean.

We need to always return a boolean and you will see the is syntax whenever something is returning a boolean but it is supplying further type information, which we can then use.

So this is a User Defined Type Guard, you can use them with your own types, the custom types (like we just saw with Song), if in your application you want to say it’s going to be a string array you can do exactly that. You have complete flexibility over what types you want to tell TypeScript are coming back.

Learn TypeScript the right way.

The most complete guide to learning TypeScript ever built.
Trusted by 82,951 students.

Todd Motto

with Todd Motto

Google Developer Expert icon Google Developer Expert

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover