How can I export the type of an Action creators return value using Flow?

Viewed 724

I'm currently exporting 2 action creators:

export const login = (payload: $PropertyType<Actions.Login, 'payload'>): Actions.Login => ({
  type: LOGIN,
  payload,
})

export const logout = (payload: $PropertyType<Actions.Logout, 'payload'>): Actions.Logout => ({
  type: LOGOUT,
  payload,
})

These are their respective `actions:

export type Login = {
  type: ActionType,
  payload: {
    username: string,
    password: string,
  },
}

export type Logout = {
  type: ActionType,
  payload: null,
}

In another file, I import the creators using the wildcard syntax

import * as Actions from ...

I'm then trying to extract from these actions the shape of their return type to use as the action type for my reducers.

What I'm trying to obtain is the following (I think, as I'm assuming this is what Flow is expecting to satisfy the reducers requirements – a type property):

type Action = { 
    login: { type: '', payload: '' }, 
    logout: { type: '', payload: '' } 
}

I've reached this function:

type Action = $ObjMap<typeof Actions, <V>(V) => $Call<V>>

Which returns the following:

Action: type Action = { 
    login: Login,
    logout: Logout
}

Which is indeed the actions returned from those creators. However, Flow within the reducer is not picking up on the type property within that action.

The reducer looks like this:

const reducer = (state: State = initialState, action: Action): State => {
  switch (action.type) {
    case LOGIN:
      return { ...state }
    default:
      return state
  }
}

With the error at action.type being:

[flow] property `type` is missing in object type [1]. (References: [1])

Any help into writing a better, generic function is much appreciated. Thank you.

1 Answers

First, import the type of the module exports from the module containing the action creators:

import typeof * as ActionCreators from './actions'

This will be an object type, so the $ObjMap utility helper can be used to map each action creator to it's type of the return value. What requires attention here is:

  • to ensure the action A returned from an action creator is exact
  • to utilize $Values to get a union of all the exact action types

The following should result in a type that contains the union of all possible actions returned by creators which can easily be refined by the { type } property in reducers:

type Action = $Values<$ObjMap<ActionCreators, <A>((...any[]) => A) => $Exact<A>>>

You can view this example on Try Flow to see it in action. Notice how for the login and logout action types, the default case of assigning the action type string to be empty ;(action.type: empty) raises errors that action.type === 'login' and action.type === 'logout' have not been handled.

Related