Redux

Redux has three major parts that should be typed:

  • State
  • Actions
  • Reducers

Typing Redux state

Typing your state object, works the same as typing any other object in Flow.

type State = {
  users: Array<{
    id: string,
    name: string,
    age: number,
    phoneNumber: string,
  }>,
  activeUserID: string,
  // ...
};

We can use this type alias to make sure reducers work correctly.

Typing Redux state immutability

Redux state is meant to be immutable: creating a new state object instead of changing properties on a single object.

You can enforce this in Flow by making every property effectively “read-only” using “covariant” properties throughout your state object.

type State = {
  +users: Array<{
    +id: string,
    +name: string,
    +age: number,
    +phoneNumber: string,
  }>,
  +activeUserID: string,
  // ...
};

Now Flow will complain when you try to write to any of these properties.

// @flow
type State = {
  +foo: string
};

let state: State = {
  foo: "foo"
};

state.foo = "bar"; // Error!

Typing Redux actions

The base type for Redux actions is an object with a type property.

type Action = {
  +type: string,
};

But you’ll want to use more specific types for your actions using disjoint unions and each individual type of action.

type Action =
  | { type: "FOO", foo: number }
  | { type: "BAR", bar: boolean }
  | { type: "BAZ", baz: string };

Using disjoint unions, Flow will be able to understand your reducers much better.

Typing Redux action creators

In order to type your Redux action creators, you’ll want to split up your Action disjoint union into separate action types.

type FooAction = { type: "FOO", foo: number };
type BarAction = { type: "BAR", bar: boolean };

type Action =
  | FooAction
  | BarAction;

Then to type the action creator, just add a return type of the appropriate action.

// @flow
type FooAction = { type: "FOO", foo: number };
type BarAction = { type: "BAR", bar: boolean };

type Action =
  | FooAction
  | BarAction;

function foo(value: number): FooAction {
  return { type: "FOO", foo: value };
}

function bar(value: boolean): BarAction {
  return { type: "BAR", bar: value };
}

Typing Redux thunk actions

In order to type your Redux thunk actions, you’ll add types for ThunkAction as a function Dispatch, and GetState. GetState is a function that returns an Object. Dispatch accepts a disjoint union of Action, ThunkAction, PromiseAction and Array<Action> and can return any.

type Dispatch = (action: Action | ThunkAction | PromiseAction) => any;
type GetState = () => State;
type ThunkAction = (dispatch: Dispatch, getState: GetState) => any;
type PromiseAction = Promise<Action>;

Then to type a thunk action creator, add a return type of a ThunkAction to your action creator.

type Action =
  | { type: "FOO", foo: number }
  | { type: "BAR", bar: boolean };

type GetState = () => State;
type PromiseAction = Promise<Action>;
type ThunkAction = (dispatch: Dispatch, getState: GetState) => any;
type Dispatch = (action: Action | ThunkAction | PromiseAction | Array<Action>) => any;


function foo(): ThunkAction {
  return (dispatch, getState) => {
    const baz = getState().baz
    dispatch({ type: "BAR", bar: true })
    doSomethingAsync(baz)
      .then(value => {
        dispatch({ type: "FOO", foo: value })
      })
    }
}

Typing Redux reducers

Reducers take the state and actions that we’ve typed and pulls them together for one method.

function reducer(state: State, action: Action): State {
  // ...
}

You can also validate that you have handled every single type of action by using the empty type in your default case.

// @flow
type State = { +value: boolean };

type FooAction = { type: "FOO", foo: boolean };
type BarAction = { type: "BAR", bar: boolean };

type Action = FooAction | BarAction;

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case "FOO": return { ...state, value: action.foo };
    case "BAR": return { ...state, value: action.bar };
    default:
      (action: empty);
      return state;
  }
}

Flow + Redux resources

© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/react/redux