TS Tricks: Type-safe Reducers
We're going to talk about how to build a type-safe reducer. Now, I use these for redux reducers, but these principles could apply to any function.
If you enjoyed my post on type guards, this one might tickle your fancy. We're going to talk about how to build a type-safe reducer. Now, I use these for redux reducers, but these principles could apply to any function.
Let's start by typing some actions.
You'll notice we've created two action types MakeFriend
and LearnSpell
. Each of these has a type
and a payload
. The payload
of each however is a different shape. In this case it's an object with a single key, but you can imagine a much more complex scenario.
Now at the bottom we have a type called WizardActions
. This is what's called a discriminated union. What this means is that we can take an argument of type WizardActions
and it will be exactly one the types in the union. Let's look at some more code to elaborate.
We now have a reducer that takes a state
and an action
and returns the current state. Don't worry, we'll handle these actions later. Now the action
argument is of type WizardActions
which means it's going to be of type MakeFriend
or of type LearnSpell
.
Let's expand on our reducer and keep going.
If you're used to redux, this should look familiar. We have a switch statement on the action type and we're handling each payload differently. For MakeFriend
actions we append a friend to the list of friends. For LearnSpell
actions we append a spell to the list of spells.
We actually don't need the default case because this is an exhaustive type-check, but it's still good to be defensive in case someone upstream uses an any
. It's also necessary if we integrate this with redux.
How does this work? Well Typescript is pretty smart. It figures out on it's own that checking type
will guarantee the payload is a certain shape. When we go to access that payload, it knows what properties to expect. If we attempted to check the spell
of a MakeFriend
action we would get a type error.
Let's have some fun now and call our reducer. First let's make some friends.
This reducer works how we expect at runtime; Harry meets Ron, then he meets Hermione. Let's learn some spells.
Now Harry learns Expelliarmus and Expecto Patronum!
If you want to know more about typing redux, I wrote an article about building a typed and code-split redux store.