TypeScript Generics and Type Guards - Explained by Example
Tags: codequality, refactorit, typescript, tutorial
Published: 2020-04-01 04:24:18


TypeScript has some powerful features that enable easy building of scalable JavaScript applications. This article describes how we leveraged two TypeScript features: generics and type guards in writing a robust utility function.

## How a utility function was born
If you had to write logic to check if an object of a certain type is defined and if the object actually has any keys, a simple way to do it in TypeScript would be:

 {% gist https://gist.github.com/meekg33k/bb16232ac0730b4469d38d9ce9c74df8 %} 

If you had to use this logic repeatedly, an ideal thing to do would be to create a function that encapsulates this logic. Written in TypeScript, this function would look something like: 

 {% gist https://gist.github.com/meekg33k/82c795d98ab87f422030ba72f4b08b3b %} 

Good stuff right? Let's consume this utility function and see what happens. The code snippet below shows what we see in a VS code editor when we use the utility function.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fnr8lkkh1yfsv2dblemt.png)
<figcaption>Typescript compiler, huh?</figcaption>

Okay, what just happened? Why are we having the error above? 

If you look closely at the code snippet above, the logic inside the function is the same logic we used before we wrote the function but for some reason, TypeScript is complaining. What are we doing wrong?


### Issue I: TypeScript doesn't "recognize" the logic in the utility function
How do we solve this issue with TypeScript not recognizing the correct logic contained within the `isPresentSomeObject` utility function?

One way to fix this would be to use the [non-null assertion operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator) (!) in TypeScript which is basically you telling the compiler, you know what you're doing.

 {% gist https://gist.github.com/meekg33k/758f37c29452043b12f593c574cf6084 %} 

While this works, I'm not a huge fan of this approach maybe because I'm lazy and don't want to type the non-null assertion operator (!) every time I have to use this function.

But seriously is there a better, more scalable way we could rewrite this? Hold that thought while we look at what the second issue with this function is.


### Issue II: The utility function does not currently support other types
Our utility function works with arguments of `SomeObjectType` type, what if we had to use the same function with another object of a different type, `AnotherObjectType`? We could refactor the utility function to support the new type by creating a union of the existing type and the new type, as shown below: 

{% gist https://gist.github.com/meekg33k/fbd96d65a1d86beb6f230db2c55c1d9d %} 

Okay, say we have to support a third type and a fourth type? Add the types to the union? Seriously? What if there was a fifth type?

You'll agree that creating a union of more types is not scalable and at this point, you may catch yourself thinking "_Why not just make the argument of type `any` so it can accept 'all' types_?"

{% gist https://gist.github.com/meekg33k/244aa2995bc123de9c2b1595ce13bee4 %} 

Err, hang on! The challenge with using type any is that we lose the type safety that comes with TypeScript. So how can we rewrite this function without having to use type `any`?


---

## Hello Generics!
According to Wikipedia, Generics are a facility of generic programming that allows you extend a [type system] (https://en.wikipedia.org/wiki/Type_system) to allow "a type or method to operate on objects of various types while providing compile-time type safety".

Imagine you had an identity function that takes an argument of a certain type and returns the argument. If you had to create one function each for a string, Boolean and number types, your code could possibly look like the code below:

{% gist https://gist.github.com/meekg33k/f0abcf17f4753173ef63bf926d3f15ff %} 

Hopefully not because the code snippet above violates the [DRY] (https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) principle. What we need instead is a way to reuse this logic regardless of argument type. Already seeing the similarity between this and our utility function? 


---

Generics provides that abstraction that allows you define parameter types (to be specified later) making it possible to reuse code with different types. With generics, the identity function above becomes:

{% gist https://gist.github.com/meekg33k/bd64c0ba43d2818cca35d0d8e1779ffa %} 

By doing this, we are telling the TypeScript compiler "_hey, the argument type is of a parameter type, **T**, and you'll find out what **T** is at compile time_". This allows us to pass any type without losing type-checking.

Rewriting our utility function to use generics, the function becomes:

{% gist https://gist.github.com/meekg33k/88293c983e37c6f0c1c13d12427fdc09 %} 

With this, you get both the dynamism that comes with using any type and the safety that comes with type checking: double win, if you ask me. 

Challenge 1 solved! Next stop, how do we get the compiler to "recognize" our utility function?




## Introducing User-Defined Type Guards
If you've used TypeScript for a while but haven't heard of type guards, you're in good company. Until we ran into this challenge, I hadn't heard of them either.
Let's imagine you have the following defined interfaces:

{% gist https://gist.github.com/meekg33k/9238959e7a9ac8c032636e591589737b %} 

If you had to implement logic that would check for what type an animal is, how would you do it? One way would be to use a [type assertion] (https://www.typescriptlang.org/docs/handbook/basic-types.html#type-assertions): 

{% gist https://gist.github.com/meekg33k/027a04b6e4ddcca64910940fdb7e0eaa %} 

Great! What happens if more types are added to the `Animal` type union?

```type Animal = Cat | Dog | Goat | Horse | Kangaroo | ...```

Writing a function for each new type isn't scalable. How then can we create a utility function that allows the TypeScript compiler infer the type of a variable without having to explicitly use type assertion? This is where type guards come to play.

From the TypeScript [documentation](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards), 
> **_a type guard is some expression that performs a runtime check that guarantees the type in some scope_**.

What does that even mean?

The gist is that with type guards, you can instruct the TypeScript compiler to infer a specific type for a variable in a certain context. 

By using a feature called [type predicates](https://www.typescriptlang.org/docs/handbook/advanced-types.html#using-type-predicates), type guards allow you to tell the TypeScript compiler that the type of an argument is <u>what you say it is</u>. This process of refining types to more specific types is called _narrowing_ and it ensures that the variable type is the exact type you expect at runtime.

Predicates are functions that return Boolean values. Type predicates are of the form `parameterName is Type`,  `parameterName` being the name of the function argument and `Type` the argument type, and they also return Boolean values.


### How do you define a type guard?
To define a type guard, simply define a function whose return type is a _type predicate_. 

So an `isCat` type guard is a function that has the **`animal is Cat`** predicate as its return type as shown below:

{% gist https://gist.github.com/meekg33k/027a04b6e4ddcca64910940fdb7e0eaa %} 

Note that I didn't even have to implement any 'serious' logic in the function, all I had to do was return `true` because remember, predicates are functions that return Boolean values.

The `isCat` type guard tells the type system that we've confirmed that the `animal` argument passed is a `Cat`, so when the compiler does its control flow analysis and sees our type guard, it says "_Oh! Whatever is passed into this function has to be a Cat if it returns true_". 

It then narrows the argument type from `Animal` to `Cat`, that way, we are guaranteed that what the type we get at runtime will be a structure that is consistent the `Cat` interface definition.

To further appreciate type guards, let's use it in an if-else block:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/864x2xyq4mwl3f909353.png)
<figcaption>Typeguards in if-else blocks</figcaption>

Remember again that we didn't implement any 'serious' logic in the `isCat` type guard, but the compiler sees the return type as a `Cat` because of the `animal is Cat` predicate; so it doesn't throw any error in the `if` block of the code snippet above. 

What happens in the else block however is more interesting.

Because the compiler expects a `Cat` type in the `if` block, it says whatever is in the `else` block has got to be a different type - that type is the [never](https://www.typescriptlang.org/docs/handbook/basic-types.html#never) type which is a type used for values that never occur, hence the error message you see in the `else` block.

Hopefully that was clear, now let us apply type guards to our utility function and see how that solves the problem we described at the start of this writing.


### Converting our utility function to a type guard
Since a type guard is just a function whose return type is a type predicate, all we need to do is to modify our utility function to return a type predicate.

What type predicate however, do we use for this utility function?

Recall that generics allow you to define parameter types which then get assigned at compile time. To convert our utility function to a type guard, we would take advantage of that and make our return type a type predicate of the form `arg is T` where `T` is the argument type.

{% gist https://gist.github.com/meekg33k/d91d2731125ea9606537b0eb6ee5cd15 %} 

And now it works!

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/w0a5spfsilb1pon4mo70.png)


## Conclusion
Thank you for reading. I hope reading this has raised your knowledge level about TypeScript generics and user type guards but more importantly, I hope it has stoked your interest in understanding how the TypeScript compiler works.

If you want to learn more about type guards, the type system or the TypeScript compiler generally, you may find the links in the Further Reading section helpful.

If you'd like to chat about this or anything at all, I'll be happy to connect via [Twitter] (https://www.twitter.com/meekg33k) or [LinkedIn] (https://www.linkedin.com/in/uduak-obong-eren). 

Stay safe 😊.


## TL;DR:
+ If you catch yourself using type any as a way of making a piece of code more robust, think generics.
+ Type guards are powerful for narrowing types, satisfying the TypeScript compiler's control flow process and guaranteeing runtime type-safety.
+ Think type guards when the type you want to use is not as specific as you'd want or when you want to verify that external data is of a specific type.
+ There are built-in type guards that come with TypeScript like `typeof` and `instanceof` but user-defined type guards can be more powerful when used right.
+ Like all things powerful, user-defined type guards shouldn't be abused; they shouldn't be thought of as a way to hack the TypeScript compiler.



## Further Reading
+ [Type guards and conditional types in typescript & preclude-ts] (http://emmanueltouzery.github.io/blog/posts/2018-04-07-prelude-type-guards.html) 
+ [How to get the types you want with TypeScript type guards | Rangle.io] (https://rangle.io/blog/how-to-use-typescript-type-guards/)
+ [Narrowing] (https://microsoft.github.io/TypeScript-New-Handbook/chapters/narrowing/)
+ [Control Flow Based Type Analysis in TypeScript] (https://mariusschulz.com/blog/control-flow-based-type-analysis-in-typescript)
+ [Type Narrowing in TypeScript] (https://medium.com/@jackhmwilliams/type-narrowing-in-typescript-44a6757c5a64)