How Does TypeScript Types Narrowing Work?

Have you ever heard about TypeScript types narrowing? Have you ever seen such an error in your TypeScript code:

TypeScript complains about "name" property not existing  on "person" object of type "object"

Even tough, you really knew that the name property exists on the person object? How did you solve this problem? Did you cast the variable to the expected type?

If the answer to at least one of those questions is yes, you might read on. There are better ways of determining the type of local variable in TypeScript. What’s more, TS compiler does a lot of stuff for us by leveraging types narrowing. In this article, we are going to see how it works and how it can be useful 🙂

TypeScript types narrowing – what is that?

One of the goals of TypeScript is to make writing JavaScript code better and more secure by adding the typing system. However, we don’t want the typing system to be problematic and making us to use the dirty haxes like all the time casting our variables explicitly, do we? 😉

First, we need to understand that TypeScript does a lot of stuff in order to ensure we work with the most specific type as possible. Basically, if the exact type of local variable can be determined, TS compiler tries to do that.

Imagine such a function:

In vanilla JS, such code would of course compile. But in our case, TypeScript sees a problem:

TypeScript complains that "lenght" doesn't exist on a variable of type "string" or "number"

The compiler is right here. Our parameter is either string or number, so the length property may not be available if someone passes a number.

You might have already been in such situations. What did you do? Well, sometimes when the code is more complex, and you just know that the variable contains the requested property, it might be tempting to cast:

Of course, we will satisfy the compiler here. But does that make our code any more safe than the previous version? Not at all.

In our example, based on the code we have written, TypeScript is not able to determine the exact type of variable. We can help it by using type guards. We will talk about them in details in a separate article. However, for now it’s enough to know that the language has some built-in type guards – one of them is typeof. Let’s use it in our function:

That’s cool, TS compiler doesn’t complain anymore. But how does it work? Our code is still accessing variable.length directly, there’s no casting there.

Notice what your IDE tells you when you hover your mouse over the variable in the first if branch after checking if the type is string:

TypeScript types narrowing figures out the type of variable is "string"

and also what it tells you when hovering over variable in the else block:

TypeScript types narrowing figures out the type of variable is "number"

Wow! The IDE magically knows what’s the exact type of our locally scoped variable! 😮

This magic is called types narrowing. TypeScript compiler does many things to ensure that the type of local variable you work with is of the most specific type possible. It is based on the complex analysis of the source code, especially the code surrounding the place in which the local variable is used.

in keyword in types narrowing

Let’s take a look at this code now:

Let’s say we have a variable which may either be Employee or Manager:

TypeScript doesn't know the type is it's not narrowed

In that place, TypeScript can’t say whether person is Employee or Manager.

Maybe you’ve used the in keyword for determining types before. Types narrowing also works with in keyword:

in keyword helps TypeScript narrow the type

As you can see, TypeScript analyzed the flow, and it noticed that in the first if block we check whether the person object contains the employees property. If it does, the compiler known it must be Manager, not Employee. Cool, isn’t it? 😉

Narrowing and equality operator

Another interesting application of TypeScript types narrowing is using the equality operator:

TypeScript types narrowing determines type of variable based on === equality operator

Notice how TypeScript determines the type of variables based on the === operator. If both parameters are equal, they must be both strings.

TypeScript types narrowing – summary

Narrowing in TypeScript is a very power feature of the language. I just showed you some simple examples, so you can grasp the idea and notice how much TS compiler does to ensure your type is the correct one.

If you’re interested, you can read much more about this in TypeScript docs. It’s however worth knowing how TypeScript compiler works and how you should properly check for types in your code. Remember that using any or explicit casting should be your last resort 😉

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x