Generic typing of Redux modules in Flow
Tags: redux, flow, javascript
Published: 2018-04-09 17:18:01

---
title: Generic typing of Redux modules in Flow
published: true
description: Creating generic types for Redux modules in Flow is not trivial, but if you don't, you'll end up with code smell.
tags: redux, flow, javascript
---

While the official Flow docs present a solution to typing Redux modules, the way their example is designed implies copy/pasting the type definitions into each of the Redux modules.  This is not optimal.

Let's examine [their example](https://flow.org/en/docs/react/redux/#toc-typing-redux-thunk-actions):

```js
type Action =
  | { type: "FOO", foo: number }
  | { type: "BAR", bar: boolean };
type ThunkAction = (dispatch: Dispatch, getState: GetState) => any;
type PromiseAction = Promise<Action>;
type GetState = () => State;
type Dispatch = (action: Action | ThunkAction | PromiseAction) => any;
```

Looking at the first three lines in above snippet, we see that their example has a static set of action types specified. This exposes an issue in applications where the state is spread into multiple modules, leading to duplicate code or other code smell.

Using the method from above snippet in such application, we have to either:

1. Specify the types top-level and provide them access to _all_ action types of our application
1. Copy/paste these type definitions for each Redux module—or, rather, the level at which you split your action types

Both of these solutions lead to code smells.

# Making Types Generic Using Generics

So how do we create these types in a generic way? Using [generics](https://flow.org/en/docs/types/generics/)!

Remember, the main problem in the original code is that the type `Action` is static. Essentially, all we need to do is to make that value variable.

Here's my solution:

```js
export type ThunkAction<S, A> = (
  dispatch: Dispatch<S, A>,
  getState: GetState<S>
) => any;
export type PromiseAction<A> = Promise<A>;

export type GetState<S> = () => S;
export type Dispatch<S, A> = (
  action: A | ThunkAction<S, A> | PromiseAction<A>
) => any;

```

> Whoah, that's a lot of generics!

Yeah, I know. But it's really not that complex:

For the purpose of following Flow's own practices and for brevity, generics are named by one letter. `A` stands for "Action" and `S` for "State". These are the two types that we have to make variable, because they're different for each Redux module.

Using generics we can require "arguments" to be passed where the types are used. Referring my solution, `ThunkAction` requires two "arguments" to be passed, State and Action, so defining a thunk action could look like this:

```js
type FetchFooActions = ActionFoo | ActionBar;
function fetchFoo(): ThunkAction<State, FetchFooActions> {
  return (dispatch, getState) => { /* inside my mind I have a digital mind */ };
}
```

`State` is the type definition for the state of our Redux module, and `FetchFooActions` is a clear specification of the actions that are expected to be dispatched from calling `fetchFoo`.

If you need to use `PromiseAction`, `GetState` or `Dispatch`, simply supply those with their generics "arguments":

```js
// Supply your State and Action types to Dispatch, State only to GetState
const cb = (dispatch: Dispatch<State, Action>, getState: GetState<State>) => { ... };
// Supply Action type to PromiseAction
function asyncAction(): PromiseAction<Action> { ... }
```

We have now untangled the four types, `ThunkAction`, `PromiseAction`, `GetState` and `Dispatch`, so they can be shared across the application without code smell. But we even enabled ourselves to be more specific in our typing simultaneously. You can see this by attempting to dispatch an unexpected action in my full example:

[Full example on flow.org/try](https://flow.org/try/#0MYewdgzgLgBAZgUysAFjAvDAFAOjwQwCcBzCASgwD4YAFQkAWwEsIEdCEIQAbANwSwBtAORQmUbgmEBdMgG4AUAoD0ymAHEEYBISbAFCAB4AHEIVhQAnsYQwAKigCuYANYBBYGPAAeAMoAaGDdqTCwFGBgAExZjfGQUAC4YABEYuNQ-QOD-cJhiJF8oOIQkzShC4r9KBQp0anwwS0UjU3MYKxtaemZWDy8wb2CMLsYWBEHKRQMTMwtrWzKKqHHfEOxa6l9mmbaO21SIWPjMoLWwiPxPJnAktxgAH3snVz7rgYDTh5GehFefYJqVBgDSaSlUMAASghIo5gDoFHtaCBoBBhm5CIR8JZvNBdGBiJMEfMYEtbJgAN65RysQgASUiSTAjgYACMdDkIqYUUkaMioBAFABfKbgv5gdrzAWIgCqxkixV5KLFw0pET2SWEjjlxQAtFz+cIOTB9RAeXyBcKlIjFfzZfLlgAxfBMSSRZUU3LqmCa7XLPXmnVwZ2u4RCkVqMWomBEzoOpCoG0QSPDO0K83Kx6J1OO4PQsWKODOK7geDxlCJrBkJIOZzuYvvIrLQJx+KJyPUVUwDhQRyEcVYaKHdIoQL5cqNhAbGCdiKgSCwE3DRDxLDCZQm5TCGAAajyBQnlZw1J09LIOCgKC0WEXdWnuQiEUHR1QWHJ0YfH4lNg1WvtCH9KKGven4msBESCvIwGCoElZAjOD5PsOr5fiU3q-rqJqBrmkRbhBigfnhuSWoKQA)

I hope this is valuable inspiration. Feel free to comment or ask questions :)