Property 'type' is missing in type 'AsyncThunkAction<fetchUserResponse, void, {}>' but required in type 'AnyAction'

Viewed 1969

Code:

import { configureStore, ConfigureStoreOptions, createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import logger from 'redux-logger';

interface fetchUserResponse {
  name: string;
}

const fetchUser = createAsyncThunk<fetchUserResponse, void>('users/fetchUser', async () => {
  return { name: 'teresa teng' };
});

const usersSlice = createSlice({
  name: 'users',
  initialState: { name: '' },
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(fetchUser.fulfilled, (state, action) => {
      state.name = action.payload.name;
    });
  },
});

interface UserState {
  name: string;
}

interface AppState {
  user: UserState;
}

const storeConfig: ConfigureStoreOptions<AppState> = {
  reducer: {
    user: usersSlice.reducer,
  },
};

if (process.env.NODE_ENV !== 'production') {
  storeConfig.middleware = (getDefaultMiddlewares) => getDefaultMiddlewares().concat(logger);
}

const store = configureStore(storeConfig);

store.dispatch(fetchUser()); // TSC throws error

TSC throws the below error when I try to dispatch fetchUser() action.

Argument of type 'AsyncThunkAction<fetchUserResponse, void, {}>' is not assignable to parameter of type 'AnyAction'. Property 'type' is missing in type 'AsyncThunkAction<fetchUserResponse, void, {}>' but required in type 'AnyAction'.ts(2345)

What generic parameters need to be passed to generic type ConfigureStoreOptions so I can pass the TSC type check.

package versions:

"@reduxjs/toolkit": "^1.5.0",
"typescript": "^4.1.2"

UPDATE

If I don't use ConfigureStoreOptions, I will get the below error when I try to set the middleware property for storeConfig dynamicly.

Property 'middleware' does not exist on type '{ reducer: { user: Reducer<{ name: string; }, AnyAction>; }; }'.ts(2339)

3 Answers

redux-toolkit maintainer here

ConfigureStoreOptions is really not a type that you should ever use by hand - it's meant to be inferred by actually calling configureStore. (I really wouldn't know how to type that by hand and I've done most of the work on the current RTK types)

This is actually the first time I've ever seen anyone do this. Is there some pressing reason that you really really have to do this? Maybe I can offer an alternative.

Having seen your updated code I'd really suggest you just do something like

const store = configureStore({
  reducer: {
    user: usersSlice.reducer,
  },
  middleware(getDefaultMiddlewares) {
    if (process.env.NODE_ENV !== 'production') {
      return getDefaultMiddlewares().concat(logger);
    }
    else {
      return getDefaultMiddlewares()
    }
  }
});

You can see it working in this playground

As @phry said, you can skip the usage of the ConfigureStoreOptions type entirely if you just call configureStore directly:

const store = configureStore({
  reducer: {
    user: usersSlice.reducer
  }
});

The piece that's missing from your storeConfig: ConfigureStoreOptions<AppState> type is the declaration of the middleware type. The current type does not include thunk or any other middleware, so you cannot dispatch an AsyncThunkAction. You would need to adjust your storeConfig type to support thunks. You do this by setting the third generic type parameter M.

Here is how the type is defined:

ConfigureStoreOptions<S = any, A extends Action<any> = AnyAction, M extends Middlewares<S> = Middlewares<S>>

You need to import the ThunkMiddleware type directly from "redux-thunk" because "@reduxjs/toolkit" does not re-export it.

import { configureStore, ConfigureStoreOptions } from "@reduxjs/toolkit";
import {ThunkMiddleware} from "redux-thunk";

const storeConfig: ConfigureStoreOptions<AppState, AnyAction, [ThunkMiddleware<AppState, AnyAction>]> = {
  reducer: {
    user: usersSlice.reducer
  }
};

const store = configureStore(storeConfig);

store.dispatch(fetchUser());

You can do this and it works, but it's honestly silly when you could just pass your options directly to configureStore and get all of the types inferred.

Ran into the same issue and I had to add middleware: [thunk as ThunkMiddleware] to my configureStore() to get the correct types for createAsyncThunk()

Related