Strongly-typed finite-state machines with Redux and TypeScript

Oliver Joseph Ash
Sep 10, 2018 · 3 min read

Finite-state machines have been all the rage recently. There are many libraries that allow you to work with finite-state machines. However, I wondered: how far can we get with our existing tools — Redux and the reducer pattern?

Note that this article is not concerned with why finite-state machines are useful. If you’re interested in learning about why they are useful, I recommend David Khourshid’s talk “Simplifying Complex UIs with Finite Automata & Statecharts”.

Reducer is a state machine

If you’re familiar with Redux, that might look familiar to you. A Redux reducer function is a state machine! A reducer function describes how the machine should transition, given the previous state and an action (aka an event in statechart terminology), to the next state.

This article is interested in how we can utilise Redux to write a strongly-typed finite-state machine, in the interests of code correctness and readability. By finite, we mean that the machine may only be in one of a finite number of states at any given time. By strongly-typed, we mean that the states and actions should carry the types of their parameters, and these parameters should only be accessible when we’ve narrowed the union of states or actions to a single variant.

The examples in this article are available on GitHub.

Setting the scene

Image for post
Image for post
Generated via https://bit.ly/xstate-viz

Prerequisites

Defining states

Note how the query parameter is only available in the Loading state, and the items parameter is only available in the Gallery state. By restricting these parameters so that they only exist in their corresponding states, we are making impossible states impossible: the type system only allows us to access the parameters when we are in a state where they can exist.

(Note there are cleaner ways to define tagged unions in TypeScript, but I refrained from using them in this example for simplicity. See the note at the end.)

Defining actions

Defining transitions

Tying it all together

This produces the following output in the console:

Going further

Our Action and State types are known as tagged union types. There are much cleaner ways of defining and using tagged union types in TypeScript, but I refrained from using them in this example for simplicity. At Unsplash we tend to use Unionize. For example, here is how we might define the Action tagged union type if we were to use Unionize:

A full example using Unionize can be seen at https://github.com/unsplash/ts-redux-finite-state-machine-example/tree/unionize.


If you like how we do things at Unsplash, consider joining us!

Unsplash Blog

Behind the scenes building the open photography movement at…

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store