7

I am trying to convert a redux project to typescript using the provided documentation:

https://redux.js.org/usage/usage-with-typescript#type-checking-middleware

However I'm having trouble doing it with my custom middleware. Here is the minimized and extracted code that causes an error for me.

store.ts:

import { configureStore } from '@reduxjs/toolkit';

import reducer from './customReducer';

import { customMiddleware } from "./customMiddleware";

const store = configureStore({
    reducer: {
        custom: customReducer
    },
    middleware: getDefaultMiddleware => getDefaultMiddleware().prepend(customMiddleware)

})

export type RootState = ReturnType<typeof store.getState>

export default store

customMiddleware.ts:

import { Middleware } from 'redux';
import { RootState } from './store';

export const customMiddleware = (): Middleware<{}, RootState> => {
    return store => next => action => {
        return next(action);
    }
}

This causes several error messages: on const store = configur...:

'store' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

on RootState export:

Type alias 'RootState' circularly references itself.

on customMiddleware export:

'customMiddleware' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

Algirdyz
  • 607
  • 6
  • 16

2 Answers2

8

In that case, you'll have to somehow break the circle.

Easiest way here is

export type RootState = ReturnType<typeof customReducer>

Edit: I think your initial code here was reducer: customReducer

With the given code it won't work - you need to split out that reducer creation before the store creation:

const rootReducer = combineRecucers({
        custom: customReducer
})

export type RootState = ReturnType<typeof rootReducer>

const store = configureStore({
    reducer: rootReducer,
    middleware: getDefaultMiddleware => getDefaultMiddleware().prepend(customMiddleware)

})
phry
  • 35,762
  • 5
  • 67
  • 81
  • Well, first there are more reducers, not just customReducer. I just made a small example. Second, if I pick one of those it doesn't work. Now it throws that there are no matching overloads when I prepend the customMiddleware. – Algirdyz Jul 05 '21 at 06:57
  • Ah OK, makes sense. If I try this I get the errors on prepend. Argument of type '() => Middleware<{}, RootState, Dispatch>' is not assignable to parameter of type 'Middleware>' and also Argument of type '() => Middleware<{}, RootState, Dispatch>' is not assignable to parameter of type 'readonly Middleware>[]'\ – Algirdyz Jul 05 '21 at 07:57
  • I tried changing `export const customMiddleware = (): Middleware<{}, RootState>` to `export const customMiddleware = (): Middleware<{}, RootState, Dispatch>` – Algirdyz Jul 05 '21 at 08:00
0

Ah, OK I figured it out. The problem was in how I was defining my customMiddleware. The documentation simply defined the export as a Middleware:

export const customMiddleware: Middleware = store => next => action => {
    return next(action);
}

But I had my export as a function that returns a middleware, since there's some initialisation there in my actual code:

export const customMiddleware = (): Middleware => {
    return store => next => action => {
        return next(action);
    }
}

So I simply had to call it as a function when prepending:

middleware: getDefaultMiddleware => getDefaultMiddleware().prepend(customMiddleware())

Very silly of me...

EDIT: using a root reducer type was needed as well.

Algirdyz
  • 607
  • 6
  • 16