TypeScript: Custom Type Guards
Tags: typescript, programming, tutorial, javascript
Published: 2021-09-07 08:46:50

Did TypeScript ever annoy you with the error `Object is possibly undefined` even if you filter out all the `undefined` values, if yes then this post is for you.

### Code Setup

Let's quickly setup a very basic example to see how we can get to such a situation.

```tsx
interface Item {
  id: string;
  name: string;
  description: string;
}

const myItems: Array<Item> = [
  { id: "1", name: "item-1", description: "bla bla bla" },
  { id: "2", name: "item-2", description: "yada yada yada" },
  { id: "3", name: "item-3", description: "bla bla bla" },
  { id: "4", name: "item-4", description: "yada yada yada" }
];

const itemIds: Array<string> = ["4", "5", "2", "6"];

const getItems = (id: string): Item | undefined => {
  return myItems.find((item) => item.id === id);
};

const items = itemIds.map(getItems);

console.log(items);
```

In the above example we have an array of `Items` and list of `itemIds` , and we are trying to get the metadata of the `itemId` . When you run this code and hover over `items` , TypeScript will tell you that the variable `items` is of type `(Item | undefined)[]` which makes sense as some id's won't match and return undefined so far so good, now the undefined items in the array are of no use to us and we don't want to deal with the undefined errors when further referring the `items` variable in our codebase, so lets quickly fix this by filtering out all the undefined values.

```tsx
const items = itemIds.map(getItems).filter(item => !!item);
console.log(items) // No undefined values in items
```

Great now we can easily loop over the items array and process it without having to deal with undefined values, let's quickly try it.

```tsx
const processItem = (item: Item) => {
  return {
    ...item,
    name: item.name.toUpperCase()
  }
}

const processedItems = items.map(item => {
   return processItem(item) // Argument of type 'Item | undefined' is not assignable to parameter of type 'Item'
})
```

Wait but why? we already handled this above, let's hover over `items` and see it's type

```tsx
const items: (Item | undefined)[] // Wait whaaat
```

The problem is that the `.filter` method will always return an array of the type with which you initially started so in our case it was `Item | undefined` which makes sense now but how do we fix this 🤔

### Type Guards to the Rescue

This is where TypeScript **custom type guards** come to rescue, a custom type guard is basically a function that's return type is a **type predicate**. It's a very simple and intuitive approach for fixing the above problem properly, let's see it in action.

```tsx
// Our type-guard
const isItem = (item: Item | undefined): item is Item => {
  return typeof item !== "undefined"
}

const items = itemIds.map(getItems).filter(isItem); //items: Item[]
```

And that's how you can filter out the undefined values properly in TypeScript with type guards and keeping the types intact in your codebase. This same approach can be extended to a use-case where you are dealing with a Union type and you want to make sure you are accessing the right properties of the instance of that type.

As always do share your thoughts on this approach in the comments below and feel free to connect with me on [Twitter](https://twitter.com/asjadanis).