Discriminated unions in Typescript: Why is it so good? šŸ¤”

Vasilyev Maksim
Frontend Weekly
Published in
3 min readJan 19, 2021

--

This article is also available in russian.

Hi! āœ‹

In this article, I want to talk about such a wonderful thing as discriminated unions in typescript (hereinafter referred to as DU). Iā€™ll share with you why they are so useful and why I love them so much.

Here are a simple explanation and documentation if you are not familiar with DU. Also, for a complete understanding of the article, itā€™s advisable to know the concepts of regular union(|) and intersection(&).

Why is it useful?

Let me give you an example. Letā€™s imagine that we have our own culinary blog. Blog visitors can leave comments on posts. The author of the comment can be anonymous, or pre-register and become a full-fledged user with a name and an avatar.

This is what the comment model looks like:

The comment authorā€™s model IAuthor can be described in various ways. Keep in mind, that only users can have a name and avatar, which they provided during registration. Anonymous remains anonymous (sorry for the tautology šŸ˜).

Attempt #1

Everything seems to be fine. name and avatarUrl are optional, which means that they may be absent. Why? Because the user can be anonymous. But is this the only reason? No.

There is a scenario that everyone forgets about ā€” a server error. Maybe someone accidentally deleted the name of a particular user from the database. Or it wasnā€™t saved during the registration at all. S**t happens šŸ¤·ā€ā™‚ļø.

The question is how do we respond to these two scenarios. If the author is anonymous, you can put the ā€œanonymā€ label above the comment text. In case of violation of data integrity on the server, you can put ā€œunknownā€, but at the same time draw an avatar. Moreover, you can send a message about invalid data to the log server, which will help you quickly find and fix the error.

Since we react to these two scenarios differently, we must distinguish them somehow. Letā€™s try further.

Attempt #2

The added kind field explicitly tells us the type of the author, and as a result, what to do if name is missing.

But there is a problem. Typescript doesn't force us to make sure that the author is a user before we reach for the name in code. The name field is always available. Seeing this, a new developer may not even realize that there is a type of author who never has a name.

Attempt #3 (Solution)

So what should we do? Use DU!

Note that name and avatarUrl are no longer optional (? is absent).

Now typescript doesnā€™t let us refer to name without checking kind. Since comment.author.kind === ā€˜userā€™ condition is true, the author is not a superposition of anonymous and user inside the if anymore. Typescript treats him as a user, allowing access to name and avatarUrl fields. The if ā€œtrickā€ is called type narrowing, and the kind field is discriminant.

Discriminated unions + intersection (&)

In all of the examples above, the kind (discriminant) was the only common field across all model variants. What if there are more common fields? In order not to duplicate them in each variant, we can ā€œfactor them outā€ using intersection (&):

Conclusion

DU allow us to describe the structure of variadic models very precisely, instead of mixing all the fields into a single ā€œFrankensteinā€ model.

IMHO, DU are often underestimated and rarely used in product development. I think the reason is that there is no such feature in the majority of popular programming languages. Therefore, developers forget about DU when they can come in really handy or do not know about them at all.

I highly recommend taking a look at the second part of this article, where I share real-world examples of DU use!

Write beautiful code! Thanks for your attention šŸ˜Š

Feel free to reach out to me if you have any questions or would like to connect, either through Twitter, LinkedIn, or just plain old email: vasilyev.maksim93@gmail.com

--

--