Redux-toolkit: Typescript issues when adding custom middleware

Created on 18 Feb 2020  路  7Comments  路  Source: reduxjs/redux-toolkit

From the docs, I can get the type of dispatch by using typeof store.dispatch, this works well with the default middlewares. However, when I add my custom middleware, the inferred type of dispatch becomes Dispatch<AnyAction>. My question is, how can I properly type the custom middleware to make everything work together?

Here is the code for my store set up:

import { configureStore, getDefaultMiddleware, Action } from '@reduxjs/toolkit';
import { ThunkAction } from 'redux-thunk';
import { createLogger } from 'redux-logger';

import rootReducer, { RootState } from './rootReducer';

const middleware = [...getDefaultMiddleware<RootState>()];

if (process.env.NODE_ENV === 'development') {
  middleware.push(createLogger());
}

function configStore(initialState: any) {
  const store = configureStore({
    reducer: rootReducer,
    middleware,
    preloadedState: initialState,
  });

  if (process.env.NODE_ENV === 'development' && module.hot) {
    module.hot.accept('./rootReducer', () => {
      const newRootReducer = require('./rootReducer').default;
      store.replaceReducer(newRootReducer);
    });
  }

  return store;
}

export type AppDispatch = ReturnType<typeof configStore>['dispatch'];

export type AppThunk = ThunkAction<void, RootState, unknown, Action<string>>;

export default configStore;

with this setup, the inferred type of dispatch is

Screen Shot 2020-02-18 at 12 09 57

when I add my custom middleware, I just change

const middleware = [...getDefaultMiddleware<RootState>()];

to

const middleware = [...getDefaultMiddleware<RootState>(), customMiddleware];

then the inferred dispatch becomes
Screen Shot 2020-02-18 at 12 12 07

This will cause ts to complain when I dispatch a thunk action
Screen Shot 2020-02-18 at 12 15 24

Here is how I define my custom middleware

const customMiddleware: Middleware<{}, RootState> = store => next => action => {
  console.log(store.getState());
  const res = next(action);
  console.log(res);
  return res;
};

I have tried the suggestions in the doc but it does not solve the problem. https://redux-toolkit.js.org/usage/usage-with-typescript/#correct-typings-for-the-dispatch-type

Could someone help me?

note: I'm using version 1.2.4

Most helpful comment

If you have to change the order of middlewares, you should annotate the middlewares type manually. For example,

 const middleware: Array<Middleware<{}, AppState> | ThunkMiddleware<AppState, AnyAction, { axios: AxiosInstance }>> = [
    ...defaultMiddleware,
    ReduxNotifier,
    setUserToTracker,
    loggerMiddleware,
  ];

All 7 comments

This seems to be a problem with TS losing some of the types in the middleware array, making it a Middleware[] type. :/

Does it help if you define it in this order?

const middleware = [customMiddleware, ...getDefaultMiddleware<RootState>()] as const;

@phryneas nope, as long as I add a custom middleware, the middleware array types change from
Screen Shot 2020-02-18 at 15 47 33
to
Screen Shot 2020-02-18 at 15 47 49

For me it seemed to be to be working in https://codesandbox.io/s/elegant-water-zyyvh , but now after a reload it isn't :/
This type erasure is deeply unsatisfying and I'm a bit out of ideas right now :(

Could you maybe ask a TS question on StackOverflow how one would best preserve all array value types here and report back?
There are some real type wizards over there, maybe one of them has a better idea.

@phryneas Thanks for the feedback. For sure I can post a question on StackOverflow but I'm not sure I can describe the problem properly since I'm just new to typescript.

I could do so too, but at the earliest 10 hours from now. I'd say just give it a try, the wizards over there are pretty good to figure out the intentions of the question ^^
Make sure to mention that it's an unintended type information erasure or something like that ^^

I found that this issue caused by the order of custom middlewares.
for example,
image

this works fine (...defaultMiddleware should be the last order)

image

but this doesn't work

If you have to change the order of middlewares, you should annotate the middlewares type manually. For example,

 const middleware: Array<Middleware<{}, AppState> | ThunkMiddleware<AppState, AnyAction, { axios: AxiosInstance }>> = [
    ...defaultMiddleware,
    ReduxNotifier,
    setUserToTracker,
    loggerMiddleware,
  ];
Was this page helpful?
0 / 5 - 0 ratings