25

I have found similar issues online but no solution for when calling a redux-thunk Action through store.dispatch().

I have the following action:

export class DBActions {
  static startDatabase(): ThunkAction<Promise<void>, {}, IClientState, AnyAction> {
    return async (dispatch: ThunkDispatch<{}, {}, AnyAction>, getState: () => IClientState): Promise<void> => {
      return new Promise<void>((resolve) => {
        dispatch(DBActions.connectDatabase())
        setTimeout(() => {
          let connection: (Connection | undefined) = getDBConnection(getState())
          if (connection) {
            dispatch(DBActions.getImports(connection))
            resolve()
          }
        }, 2000)
      })
    }
  }
}

This works without problems when added through mapDispatchToProps in a component, but not when directly called inside my store.ts after defining a store. store.dispatch(DBActions.startDatabase()) leads to:

TS2345: Argument of type 'ThunkAction<Promise<void>, {}, {}, AnyAction>' is not assignable to parameter of type 'AnyAction'.
  Property 'type' is missing in type 'ThunkAction<Promise<void>, {}, {}, AnyAction>'.

Any help and suggestion is appreciated!

Shin
  • 678
  • 3
  • 12
  • 22
  • 7
    At first glance, your type annotations look correct. Can you try combinations of typing `dispatch` to `Dispatch` from 'redux' and calling via `dispatch(DBActions.connectDatabase())`, or keeping your annotation for `dispatch`, but calling via `dispatch(DBActions.connectDatabase())`, or even via `dispatch>(...)`? – Yakimych Jan 27 '19 at 22:29
  • 6
    Oh wow, you are right. It really was that simple! `store.dispatch(DBActions.startDatabase())` worked :) Thanks a lot! – Shin Jan 29 '19 at 18:34
  • 2
    `store.dispatch` looks like a hack rather than a solution IMO. How do you initialize the store? – bonbonez May 23 '20 at 07:10

4 Answers4

11

An easy workaround to avoid the error message - though it is not a solution is:

dispatch<any>(DBActions.getImports(connection))
Rambatino
  • 4,716
  • 1
  • 33
  • 56
10

You can extend the Dispatch interface of redux using Declaration Merging of TypeScript. Extend the Dispatch interface with a ThunkDispatch overload signature. Then store.dispatch() method can dispatch async action.

store.ts:

import { applyMiddleware, createStore } from 'redux';
import thunk, { ThunkAction } from 'redux-thunk';
import { DBActions } from './action';

declare module 'redux' {
  interface Dispatch<A extends Action = AnyAction> {
    <S, E, R>(asyncAction: ThunkAction<R, S, E, A>): R;
  }
}

const store = createStore((state) => state, applyMiddleware(thunk));

// dispatch async action
store.dispatch(DBActions.startDatabase()).then(() => {
  console.log(store.getState());
});

// dispatch sync action
store.dispatch(DBActions.connectDatabase());

package versions:

"redux": "^4.1.0",
"redux-thunk": "^2.3.0"
"typescript": "^4.2.4"
Lin Du
  • 88,126
  • 95
  • 281
  • 483
1

I fought with this for a while, trying different combinations of typings and could not solve it.

It turns out the issue was because I had redux@4.0.5 and redux@4.1.0 installed next to each other.

To fix the issue I just did: yarn install redux@^4.1.10, which removed redux@4.0.5 from my yarn.lock file and resolved the issue!

Neil Devas
  • 21
  • 8
0

I'm not sure whether this is correct answer or not. The following is my code:

type MyThunkDispatch = ThunkDispatch<{}, {}, AnyAction>;
const thunkDispatch = store.dispatch as MyThunkDispatch;

thunkDispatch(DBActions.startDatabase());
joseph
  • 71
  • 2
  • 5