4
votes

Redux Promise Middleware seems to handle the promise resolution of an action payload. For example, consider this action creator that uses a Promise as payload:

export default class ActionCreators {
  public static fetchAllUsers(): FetchAction {
    return {
      type: actionTypes.FETCH_ALL_USERS,
      payload: fetch("api.com/users")
    };
  }
}

When using this action creator, TypeScript expects an Action returned data, instead of the Promise that is actually returned because of redux-promise-middleware.

So the following code will not compile because Property 'then' does not exist on type 'Action'.

dispatch(ActionCreators.fetchAllUsers())
    .then(response => {
        // do something with response
    })

Typescript expects returned properties like payload and type, which are actually not returned because the promise has been handled already and has ACTUALLY been returned.

.then is ready to be used, but typescript will not compile because it expects something else.

I guess the question is: How to write the correct typings to handle redux-promise-middleware actions, while providing redux' connect-function with the correct dispatch types for mapDispatchToProps etc.

1
I'm pretty new to redux, but doesn't this violate the pure function principle of redux? If the payload is a promise that fetches data from a server, then the reducer that processes this payload can't be a pure function, since the results aren't deterministic.wired_in
This is true. And this makes it even more annoying that there's no clear way to use this middleware. We're considering just moving on to an other solution.Stef van Wijchen
Did you find a solution to this problem? I encountered the very same problem, and I couldn't find a solution. In my case, I cannot use dispatch(...).catch notation. it says Type error: Property 'catch' does not exist on type '{ type: string; payload(): Promise<AxiosResponse<any>>; }'metoikos
@wired_in no, because the middleware swallows that original action containing the promise payload, dispatches an immediate PENDING action, then later a FULFILLED / REJECTED action depending on what happens with that promise. These actions are all pure and deterministic, and they're the ones that actually go through to the reducers.davnicwil

1 Answers

1
votes

You can use conditional types. E.g.

export interface BaseAction<TPayload> {
   type: string;
   payload: TPayload;
}

export interface Dispatch {
    <T extends BaseAction<any>>(action: T): 
         T extends BaseAction<Promise<infer R>> ? Promise<R>:  T

}