Discriminated Unions — Are You Using Them?

Discriminated Unions — Are You Using Them?

Photo by Beth Macdonald on Unsplash

Let’s discuss a common coding situation. We call an HTTP service and want to set a loading flag. So we create a simple isLoading variable that will tell if we are waiting on the service to respond.

The Problem

isLoading is great until that fateful day when we get a service error. Now we need to stop the spinner and return an error. So we add a httpError variable, and everything is good again, but something nags at you…

Now we have a variable that tells whether to display the spinner and a variable with an error. However, we have to constantly check httpError if we want to know if we have an error. Worse, if we don’t clear httpError, we don’t know if the error was from this or a previous call. Things are quickly getting out of control.

As intelligent developers, we create an object representing a result from the call to the HTTP service. Our first attempt looks like this:

https://medium.com/media/6e6409e93de186b1185528ead3f39dcd/href

This object works well. We can use this object to track the status of our HTTP service. Calls to the service can return a HttpServiceStatus, and we can handle the result. This practice is a standard solution, but there is a problem. We need knowledge of the type to construct. For example, we can construct the following “results” that are not good. Consumers can abuse our type.

https://medium.com/media/cb1818d03249b40ac91313811d8c3d8e/href

Now we have a new ask. The service received an upgrade that returns some data when successful. Our HTTP service is much more popular. At some points, it gets overloaded and will produce a “retry” status with an amount of time to wait before retrying. We upgrade our HttpServiceStatus to handle these new cases:

https://medium.com/media/2afa5c0bc4558f34ade15bd99efa7a37/href

As you can see, things have gotten worse. With each new feature, the number of ways to make mistakes grows. Now we need to know what all the flags mean to understand how to create or consume an HttpServiceResult. It would be great if we could make it impossible to create bad states. It would be fantastic to know the proper shape of a state when consuming it.

The Solution

Luckily, discriminated unions are here to the rescue. What are discriminated unions, and how can they help solve this problem? Discriminated unions are a union type with a property that can be used as a discriminant. You can read more about this in the documentation for discriminated unions.

We can take this and apply it to our problem. We will create a few states that will handle our various service results.

https://medium.com/media/fe400cedfe8d7f8604d1d891f7c511be/href

Note that every type has a state property that will become our discriminant. The name of the discriminant doesn’t matter. What is important is that this field exists on every type AND is unique. With these types, we can create our union:

https://medium.com/media/046e66f9d2379d7d2df2b9968b84a7df/href

How does this union help us? First, let’s create a function that will accept our union, and we will run some tests.

https://medium.com/media/4bc23a0abaab427de2ff63b21b2946db/href

We can use the discriminant inside the function to determine how to proceed. By checking this field, we can strongly type our code, and Typescript will give us intellisense into the available properties.

Another benefit is we made it impossible to construct bad states. Here are some examples of errors from Typescript Playground.

Errors from Typescript Playground.

This example is a common situation that can be solved using discriminated unions, but they can help elsewhere. For example, let’s say we want to load some components dynamically. We can create a discriminated union of the components so our loading code can deal with all components.

In many cases, our code would benefit from a union instead of using flags. If that union needs to add extra data, we should investigate if a discriminated union is the proper use case.

Here is the Typescript Playground I used for these examples.

Join us at ng-conf 2023!

ng-conf | June 14–15, 2023
Workshops | June 12–13, 2023
Location | Salt Lake City, UT

ng-conf 2023 is a two-day single track conference focused on building the Angular community. Come be a part of this amazing event, meet the Angular Team and the biggest names in the community. Learn the latest in Angular, build your network, and grow your skills.

Discriminated Unions — Are You Using Them? was originally published in ngconf on Medium, where people are continuing the conversation by highlighting and responding to this story.

Leave a Comment

Your email address will not be published. Required fields are marked *