User defined Type Guards in TypeScript blog post

User defined Type Guards in TypeScript

Todd Motto

13 Aug, 2019

TypeScript

4 minutes read

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!

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.

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.

About the author

Todd Motto profile picture

Todd Motto

GDE Google Developer Expert

Todd is the Founder of Ultimate Courses. With a passion for Angular, TypeScript and JavaScript, Todd leads the online courses creation and has written hundreds of articles on front-end web development and beyond. He specialises in breaking down complex topics and understands the critical mission of learning new technology fast, comprehensively and the right way.

Love the post? Share it!

Lots of time and effort go into all our blogs, resources and demos,
we'd love it if you'd spare a moment to share them!

Explore our TypeScript courses

Get started today and join over 60,000 developers.