Creative Uses of TypeScript Discriminated Unions
Tags: typescript, angular, functional
Published: 2020-03-08 22:46:31

---
title: Creative Uses of TypeScript Discriminated Unions
published: true
date: 2020-03-08 22:39:36 UTC
tags: TypeScript, Angular, Functional Programming
series: Doggo Quest
cover_image: https://i1.wp.com/killalldefects.com/wp-content/uploads/2020/03/ContextJsonComplex.png?fit=768%2C358&ssl=1
canonical_url: https://killalldefects.com/2020/03/08/creative-uses-of-typescript-discriminated-unions/
---

The post [Creative Uses of TypeScript Discriminated Unions](https://killalldefects.com/2020/03/08/creative-uses-of-typescript-discriminated-unions/) appeared first on [Kill All Defects](https://killalldefects.com).

---

Let me show you how creative use of TypeScript’s [discriminated unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions), type aliases, and functions can give you a greater degree of flexibility in your own code.

I’m going to do this by illustrating how these techniques addressed a problem that I was trying to solve and then talk about some additional ideas on how these techniques can be applied.

## The Problem I’m Solving

I’m building [a text-based game](https://killalldefects.com/2020/02/01/game-design-with-event-modeling/) for a few talks I’ll be giving this spring. This is the type of game where the game engine describes something and then the player types in a command such as `look at the flowers` or `hit the mushroom with the glowing red hammer`.

In order to do that, I need to be able to represent parts of the game world as objects that have basic responses to verbs that the player can try.

This is fairly easily represented in a simple object where verbs with custom responses are defined as string properties on the object (_note: the player is a dog in my game_):

![](https://i2.wp.com/killalldefects.com/wp-content/uploads/2020/03/SimpleObjectJson.png?resize=770%2C135&ssl=1)

So, using the above object, the engine will give default responses to verbs that are not defined on that object (e.g. eat or pick up).

But what if I wanted a verb to actually impact the game world? I’d need something a bit more flexible.

This is where discriminated unions come into play.

## What is a Discriminated Union?

Discriminated unions in TypeScript are a way of telling TypeScript that something is going to be one of a fixed number of possibilities.

For example, if I declare a parameter as `x: string | number | boolean`, I’m telling TypeScript that it should expect `x` to be either a `string`, a `number` or a `boolean`. TypeScript will then check my usage of `x` to make sure I’m working with properties that are common on each one or doing necessary type checking / casting.

The reason this can be helpful is that it allows you to specify simple paths and complex paths for things depending on what value is passed in.

## Defining the Types

Now that we’ve established what discriminated unions are and why they might be helpful, let’s take a look at my definition of a `GameObject`. This is an object in the game world that the player can potentially interact with.

{% gist https://gist.github.com/IntegerMan/64d9badaaa6734f792b9b7dce9e00ce6 %}

Focus first on `ObjectResponse` on line 4. This is the type used to represent a verb handler on an object. For example, how a `shoe` might respond to the `eat` verb.

I define `ObjectResponse` as a `ContextAction | string` meaning that it will either be a simple string indicating a description to print out or it will be a more complex response represented by `ContextAction`.

`ContextAction` is a custom type definition that represents a function taking in a `CommandContext` object (a custom domain object representing the current state of the world and some basic response formatting capabilities) and returning `void` (nothing).

What this is essentially saying is that a verb handler on an object will either return a simple string description or it will be a more complex function that takes in a `CommandContext` object and does something with it.

Here’s a practical example that uses the two signatures to do more complex logic in its `push` and `look` verb responses:

![](https://i1.wp.com/killalldefects.com/wp-content/uploads/2020/03/ContextJsonComplex.png?resize=770%2C359&ssl=1)

## Interpreting Values of Different Types

Okay, so if we’re dealing with something that is a discriminated union, how do we effectively work with it?

Take a look at this TypeScript snippet:

![](https://i0.wp.com/killalldefects.com/wp-content/uploads/2020/03/CommandHandler.png?resize=728%2C455&ssl=1)

Here we query the `gameObject` to see if it has a property named the same thing as `verbName`. If that’s _not_ present we’ll just add the generic response for the verb.

If the response _is_ present, we know it to be either a `string` or a `ContextAction` so we can switch off of the type of that value and handle it appropriately.

Here we respond to `string` values by casting the value to string and using a method on the `context` to add a simple message.

If the value was _not_ a `string` then we know it’s going to be a `ContextAction` and will be a function with a signature that takes in a `CommandContext` and returns `void`. In that case, we simply invoke the function and pass in the `context`.

## Closing Thoughts

Hopefully this illustrates the usefulness of discriminated unions and type aliases in handling a variety of scenarios and potentially gives you ideas for how to streamline some of your existing logic.

I personally consider discriminated unions and working with functions when I see patterns of similar data or the potential for a high amount of boilerplate or repetitive code.

Discriminated unions do add complexity to your code, but that price _can_ be worth it in simplicity and flexibility in other areas. Ultimately the decision is up to you and will change based on what problems you’re trying to solve.