Typescript: User Defined Type guards
Tags: typescript, programming, webdev
Published: 2022-12-18 12:56:42

In our previous post [any vs unknown](https://dev.to/sachitsac/typescript-unknown-vs-any-fld), we talked about type narrowing. Basically type narrowing is:

> The process of refining types to more specific types than declared is called narrowing. 

[Source](https://www.typescriptlang.org/docs/handbook/2/narrowing.html)

For javascript primitives ( string, number, bigint, boolean, undefined, symbol, null) that can contain values, we can use the special typeof operator to narrow the type. For example:

```
// ensuring age is a number if we don't know the value
// eg. it might be an any or unknown
if ( typeof age === 'number' ) { ... }
// or
if (typeof signature === 'string' { ... }
```

This is all good when we have a primitive, but what if we have an object, how do we use the same technique of narrowing. But before that, why would we even need to do that ?

So here's an example:

```
interface Pet {
  speak: () => void;
}

interface Fish extends Pet {
  swim: () => void;
}

interface Bird extends Pet {
  fly: () => void;
}

interface Cow extends Pet {
  milk: () => void;
}

// Essentially, the subject is a union type of Cat or Dog
function flyPet(subject: Fish | Bird | Cow ) {
  subject.fly();
}
```

So the subject variable passed to the function `flyPet`, is a union type of Bird or Fish or Cow. Other than the speech, each pet have their own trait. For example, fish can swim and bird can fly.

So if we invoke the function `flyPet`, then we will get a compile time error:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jabxl61vp7db8w3t4aoz.png)

But this is good when we know the subject type. Now to why we need this, imagine if we didn't know the type of subject, eg. it was `any` or `unknown` as the data might be coming from an external api or its legacy code that we don't know what the type is genuinely, that would result in a run time error instead of catching it at compile time.

```
function flyPet(subject: any ) {
  // compiler happy, but will result in a runtime error
  subject.fly();
}
```

We can prevent those runtime errors by using the technique of type narrowing by creating [user defined type guards](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates). e.g.

```
function isBird(obj: any): obj is Bird {
  if (!obj.hasOwnProperty('fly') || !(typeof obj.fly === 'function')) {
    return false;
  }
  return true;
}
```

Function breakdown: We assert that for an object to be a bird

- It needs to have a property `fly`
- And the property `fly` is an executable function

Now we can rewrite our `flyPet` function to be run time safe like this:

```
function flyPet(subject: any ): void {
  if (isBird(subject)) {
    subject.fly();
  }
}
```

So when we use our type guard, we have basically narrowed down the type to be a bird and anything inside the if scope, will be a bird and hence we can use our subject to fly.

In summary, User defined Type guards allows us to:

- Narrow down the type for objects
- Prevent runtime errors that are not caught compile time.
- Practice safe programming techniques
- Catch errors and not break the user application / experience.

[In the next post](https://dev.to/sachitsac/typescript-type-guards-with-zod-1m12), we will look at the concept of User defined type guards using a very popular library called [Zod](https://zod.dev/) and how it can make it easier to write user defined type guards.

Till then, happy guarding.