Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
TypeScript typings power, when used properly, could help us write better code. This article is about how to use TypeScript type system with Redux to create fully typed state management store.
I assume that you already know redux and maybe a little bit of TypeScript. I appreciate any inputs, I only tried implementing this recently and I am open to any improvements. This is a highly opinionated and subjective article.
I used to write Redux in JS, and then define its actions and types as constants. It is not unusual for me to see something like this.
The problem with the code above was, well, `AUTH_TYPES` can’t be changed as a whole, but this could happen.
Well, we can fix it by actually making those constants, constant.
Now we don’t have to worry about accidentally (or on purpose) changing the action type constants.
Now our reducer can refer to those constants on the switch cases, and everyone is happy!
Now imagine that we added another action type, but we forgot to handle it on some reducer that depends on it. Meh, don’t worry, we have someone that keeps the system running every 108 mins.
Even though the chance of forgetting to add an action type handler is low (I mean the moment you decided to add an action type is also the moment which you have to add its handler too), we will see how TypeScript can help us ensuring that all of the action type is handled properly.
I personally think that the amount of boilerplate that we have to write when using Redux is huge, and when maintaining huge boilerplate codes, we might end up with a lot of inconsistencies (multiple action types, reducer handling wrong action type). This is where TypeScript comes to the rescue (I am not saying that you would not be writing boilerplate code anymore though).
Redux, Meet TypeScript
Now, based on the code above, we can see that we have these problems:
- Action Type Constants is hard to maintain.
- We have to write a lot of action creator to wrap those action type constant.
- There might be inconsistencies.
- We might forgot to handle new action type on our reducers.
You might not encounter all of those problems, well, I only encountered number 1, 2, 3, and 4 (that’s all by the way).
Now, let’s try to solve those problems.
Problem #1 — Use TypeScript Types instead of Constants
Now, even though I am using TypeScript and Redux, I used to be writing the same code as above, on which I defined my action types as constants. Now, instead of doing that, I tried to throw away those types constants, and use TypeScript types instead.
We define the interface AuthStore to be our store schema, which mean that is how our store will looks like.
Below it is how we define the action types constants. We are using TypeScript interface to actually define how the action will looks like instead of just having the action type name as string constant.
Now you might wonder, what is PayloadedAction<”auth/set-token”> and Action<”auth/flush-token”>? Well, it is a simple interface that looks like this:
So, SetTokenAction would actually looks like this:
And FlushTokenAction is just an action without any payload.
The reason we are creating Action and PayloadedAction separately is so that we can have an action creator that is properly typed. You will read more about it below.
Now our constants is defined as a type, and we can refer to them as its type.
Wait, but we are writing the constants as a literal string there! What if we mistyped? Well, you can’t, TypeScript checks and ensure that only “auth/set-token” or “auth/flush-token” is written there.
If you are using Visual Studio Code, it will tell you what string constants it accepts there. Actual string constants as a constants!
Now we are sure, that the reducer will handle exactly types that we defined for TokenActions. The use of AuthStore[“token”] also ensure that the return type of the reducer matches our store schema.
Problem #2 — We Create Our Own Type Aware Action Creator
Now, we still need to define the action creator one by one, but at least we will create an action creator’s creator that is type aware and matches our original interface definition.
It means that createPayloadedAction should accept a payloaded action type definition, and the type and payload that it will create will match with the type definition provided.
The createAction is just the same, only without payload.
The setTokenAction above will become a function that looks like this:
And it is type aware, you will get code completion correctly.
If you don’t like defining an action creator, you can still dispatch an action that is type aware by providing its type when calling dispatch.
Problem #3 — Well, What We Did Above Solved This
When we are using TypeScript to check everything that we type, inconsistencies could be avoided.
Now everytime a reducer accepts a new action, we must define the type of the action, then we will have to update the reducer, and because TypeScript checks everything, we will have a very small chances of having inconsistencies.
If the type is not included as TokenActions, we will get an error, and if the return type doesn’t fit our schema, we will also get an error. Error during development is a bliss, error during production is a blast.
Problem #4 — Solved, TypeScript Checks Whether You Cover All Switch Cases or Not
No step is needed to be done here, the way we structured and typed our code helped us avoiding this problem.
TypeScript understands that inside the default case here, all action types were already handled, so if you tried to type “action” and then open code completion, it would not suggest you anything else. This way, you know that you handled all of the code cases (the type would become never).
Now there are some things that TypeScript didn’t check, example is when we have duplicate switch cases on our reducer:
However, we can also use TSLint to prevent that, with this option enabled:
"no-duplicate-switch-case": true
Conclusion
TypeScript type system is powerful, we actually can remove those action types constants and those boring action creator boilerplate code with something that actually provide us with typed system that helped us ensuring our code is consistent.
The code used on this example can be viewed at https://github.com/adityapurwa/typescript-redux
Hope we all learned something from this article!
Aditya Purwa
Writing Better Redux’s Code with TypeScript was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.