Redux is great. TypeScript is great. Redux isn’t, at the outset, anywhere where near typesafe. This means Redux in TypeScript isn’t so great on its own. However, given a little effort, it is possible to use Redux in an (almost) totally typesafe way using TypeScript. Here follows a complete guide to how I got it working. This approach uses some configuration which can’t be statically verified by the typescript transpiler. However, that configuration is pretty minimal and with it the rest of your app can benefit from completely typesafe access to redux. I’ve separated it into three parts:
- Typesafe Actions
- Typesafe Store Configuration and Reducers
- Connecting to components
Part 1: Typesafe Actions
I found this clever implementation for typescript actions on a redux github issue posted by aikoven, I’ve modified it slightly. If you’re not using TypeScript 1.9 (and you’re probably not since at the time of writing it was unreleased), just remove the
There’s three things to notice here:
First, through the
Action interface, we define a typesafe way to access redux actions before we know what action we’re dealing with.
Second, through the
isType<T> function, we expose a typesafe way of accessing action payloads through use of a type guard. It works by comparing the action type to the one which has been defined in the action creator. This is safe so long as the action types are globally unique. An example of how this function is used is included in part 2.
Third, the action creator parameters themselves have defined types. This closes the loop and makes the entire action type safe. This is especially effective with
noImplicitAny enabled in your TypeScript config!
Part 2: Typesafe Store Configuration and Reducers
Here’s an example of the above actions integrated into a typesafe redux reducer:
Beyond the use of the
isType<T> type guard, take note of the way the reducer is defined. The type of the state parameter and return value are both defined to be
ListItems which is
Immutable.List<ListItem>. As far as I can tell, there’s no way to get the TypeScript compiler to help you here. You have to add these two type declarations yourself, but that ensures that the rest of the reducer is statically verifiable.
ListItemsState interface can be used to define the type of the global state demonstrated in the following example. This particular example defines a global singleton store. Whether you want to do this is up to you. The approach I suggest here certainly doesn’t require it.
The store exposes three functions:
getState()method. This method is the crux of the implementation: it returns redux’s state defined as the
Statetype. We’ve declared
Stateas a union of state interfaces defined by individual reducers. In this case, it’s the union of
StickiesStatewe defined in
dispatch()method which wraps redux’s
getState()but accepts only actions of type
subscribeToState()method which wraps redux for convenience.
It’s important to realize that the compiler can’t help you with your definition of
State. If the types in
store.ts are misconfigured the application will be misconfigured as well. However, get this right and the rest of your application will benefit from typesafe access to the redux store.
Part 3: Connecting to components
Finally, you’ll also need to connect redux to your react components. I suspect most people will choose to react-redux to do this. It isn’t trivial to use react-redux in a typesafe way, though. It’s also not a terribly complicated library so I’d suggest you consider using something like the following:
This implementation has the distinct advantage of making it easy for the typescript transpiler to verify the typesafety of your code. A simple use looks like this:
It’s taken a little work to get here but the transpiler is capable of verifying the entire above example! Access to the store is completely typesafe for all components. Woo!
If you use this approach, you’ll need to figure out how to do your own equality comparison in
stateConnector.ts. This depends on your redux implementation (such as use of immutable.js, seamless-immutable, etc) and can have significant performance implications. For most projects a relatively simple implementation should be just fine, though.