1

I'm attempting to configure the dispatch type on a redux store that is using thunk middleware and an optional middleware logger (redux-logger).

This correctly infers the thunk type on store's dispatch...

import { createStore, applyMiddleware } from 'redux';
import thunk, { ThunkMiddleware } from 'redux-thunk';

// ...

const preloadedState = undefined;
export const store = createStore(rootReducer, preloadedState, applyMiddleware(thunk as ThunkMiddleware));

enter image description here

When I expand the middleware to include a conditional logger and spread an array of middleware into applyMiddleware, the store's dispatch is not correctly inferred.


import { createStore, applyMiddleware, Middleware } from 'redux';
import thunk, { ThunkMiddleware } from 'redux-thunk';
import { createLogger } from 'redux-logger';

// ...

const middleware: Middleware[] = [thunk as ThunkMiddleware];

if (Settings.environment === 'development') {
  const logger = createLogger({ collapsed: (_getState, _action, logEntry) => !logEntry.error });
  middleware.push(logger);
}

const preloadedState = undefined;
export const store = createStore(rootReducer, preloadedState, applyMiddleware(...middleware));

enter image description here

This is driving me crazy, any thoughts as to how to fix the typing issue when spreading the middleware array?

Roy Kolak
  • 605
  • 1
  • 6
  • 18

2 Answers2

4

This is where redux-toolkit comes in real handy.

Their docs mention to do it this way

import { configureStore } from '@reduxjs/toolkit'
import { useDispatch } from 'react-redux'
import rootReducer from './rootReducer'

const store = configureStore({
  reducer: rootReducer,
})

export type AppDispatch = typeof store.dispatch
export const useAppDispatch = () => useDispatch<AppDispatch>() // Export a hook that can be reused to resolve types

export default store
Chris Sprance
  • 302
  • 2
  • 12
  • Adding to this: yes, this will take correctly typed middlewares into account. – phry Nov 07 '21 at 21:52
  • Ah okay, I can definitely see the benefit of using `toolkit` with thunk integrated and the dispatch type correct by default. Adding on to this, I was able to add the `redux-logger` via `return getDefaultMiddleware().concat(logger)` in the `middleware` callback, but the `dispatch` type was still getting cleared. The key was adding types for `redux-logger`... `yarn add @types/redux-logger`! – Roy Kolak Nov 08 '21 at 14:16
1

You are doing const middleware: Middleware[] = ... which explicitly eliminates the specific types stored in the array and only retains that they are Middleware, instead do const middleware = [...] as const to retain as much information about the contents as possible. (and then use type assertions to allow the list to be modified without retaining those types since you can't possibly rely on then anyway)

Tadhg McDonald-Jensen
  • 20,699
  • 5
  • 35
  • 59
  • Ah interesting, can you expand on "and then use type assertions to allow the list to be modified without retaining those types since you can't possibly rely on then anyway"? – Roy Kolak Nov 07 '21 at 16:06
  • when you do `as const` it labels it a readonly array so methods like `push` just aren't defined, so overriding this to get a logger in there would give a slightly incorrect type when the logger is added. The more correct solution would be to do `const middleware: (ThunkMiddleware | LoggerMiddleware)[]` so it has enough information to infer dispatch correctly as well as allowing you to push the logger but in general that is a pain. – Tadhg McDonald-Jensen Nov 07 '21 at 16:10
  • I posted the answer then realized why you were putting in that list to begin with and realized the answer as written wouldn't work so made a hasty amendment – Tadhg McDonald-Jensen Nov 07 '21 at 16:12