Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.3k views
in Technique[技术] by (71.8m points)

redux - Typescript, ReduxJS Toolkit - Reducer generated by createSlice is not assignable to parameter of type 'Reducer<unknown, AnyAction>'

I'm new to typescript and having an issue understanding an error. I'm attempting to use a reducer generated by @reduxjs/toolkit's createSlice and feeding that into a function with the following definition:

// .d.ts file
export default function persistReducer<S, A extends Action = Action>(
  config: PersistConfig<S>,
  baseReducer: Reducer<S, A>
): Reducer<S & PersistPartial, A>;

// function definition
export default function persistReducer<State: Object, Action: Object>(
  config: PersistConfig,
  baseReducer: (State, Action) => State
): (State, Action) => State & PersistPartial { /* ... */

Which is returning the following error:

Argument of type 'Reducer<CoreState, AnyAction>' is not assignable to parameter of type 'Reducer<unknown, AnyAction>'.
  Types of parameters 'state' and 'state' are incompatible.
    Type 'unknown' is not assignable to type 'CoreState | undefined'.
      Type 'unknown' is not assignable to type 'CoreState'.ts(2345)

However, it works when I cast the reducer as the Reducer type provided by redux/toolkit.

// fails
persistReducer( persistConfig, coreReducer )

// succeeds
persistReducer( persistConfig, coreReducer as Reducer)

As far as I can tell, the reducer already has that type and I'm not sure why I need to cast the value before sending it along. What am I missing? Any help would be appreciated!

Here's my slice definition

import { createSlice, PayloadAction } from '@reduxjs/toolkit';

export type User = {
  id: string,
  name: string,
  avatar: string,
  token: string
}

export type CoreState {
  isSignedIn: boolean,
  user: User | null
};

const defaultCoreState : CoreState = {
  isSignedIn: false,
  user: null
};

export const slice = createSlice({
  name: 'core',
  initialState: defaultCoreState,
  reducers: {
    setUser: (state: CoreState, action: PayloadAction<User | null>) => {
      state.user = action.payload;
      state.isSignedIn = action.payload ? true : false;
    }
  }
});

export const { setUser } = slice.actions;
export default slice.reducer;

Edit: a reducer created using combineReducers triggers the same error, and is alleviated with the same workaround. It seems to me that persistReducer function is having trouble inferring the type even though it has imported same Reducer type definition from the same library. What gives?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Thanks to the generous help of @phryneas on the Reactiflux discord, the issue has been solved.

export default function persistReducer<S, A extends Action = Action>

Should become:

export default function persistReducer<S, A extends AnyAction>

Having seen the word "Action" and "Any" so many times in all the definitions I combed through while debugging this, I missed the difference. AnyAction is the correct type.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...