1
votes

When I'm trying to get the states through mapStateToProps function, it's giving me property does not exist on type never

This is the snippet code of setup for the reducers, I'm also using redux saga and redux persist here

// combine all reducers
const rootReducer = combineReducers({
  dashboard: dashboardReducer,
  sales: salesReducer,
  auth: authReducer,
});

export type RootState = ReturnType<typeof rootReducer>;

// set config to our rootreducer
const pReducer = persistReducer(persistConfig, rootReducer); //ERROR ON rootReducer HERE

const sagaMiddleware = createSagaMiddleware();
// use the new persistreducer in creating store
const store = createStore(pReducer, composeEnhancers(applyMiddleware(sagaMiddleware)));

const persistor = persistStore(store);

Note: rootReducer returns error when I'm setting persist config using persistReducer.

Below is the error when I hovered over rootReducer in the persistReducer function

const rootReducer: Reducer<CombinedState<{
    dashboard: never;
    sales: never;
    auth: never;
}>, AppActions>
Argument of type 'Reducer<CombinedState<{ dashboard: never; sales: never; auth: never; }>, AppActions>' is not assignable to parameter of type 'Reducer<unknown, AppActions>'.
  Types of parameters 'state' and 'state' are incompatible.
    Type 'unknown' is not assignable to type 'CombinedState<{ dashboard: never; sales: never; auth: never; }> | undefined'.
      Type 'unknown' is not assignable to type 'CombinedState<{ dashboard: never; sales: never; auth: never; }>'.
        Type 'unknown' is not assignable to type '{ readonly [$CombinedState]?: undefined; }'.ts(2345)

Then I tried to get rid of the error by doing this, which obviously doesn't work out well and it led to the type never errors afterwards, but other than doing this, I'm not sure how else to suppress the error.

const pReducer = persistReducer<RootState>(persistConfig, rootReducer);

Then in one of my TSX file when I tried to get the states

interface StateProps {
  loading: boolean;
  authenticated: boolean;
  errorMessage: string | null;
  userInfoObj: TReceivedUserInfoObj | null;
}
const mapStateToProps = (state: RootState): StateProps | void => {
  return {
    loading: state.auth.loading,
    errorMessage: state.auth.errorMessage,
    userInfoObj: state.auth.userInfoObj,
    authenticated: state.auth.auth_token !== null,
  };
};

And it's giving me type never errors

Property 'loading' does not exist on type 'never'.  TS2339

    108 | const mapStateToProps = (state: RootState): StateProps | void => {
    109 |   return {
  > 110 |     loading: state.auth.loading,
        |                         ^
    111 |     errorMessage: state.auth.errorMessage,
    112 |     userInfoObj: state.auth.userInfoObj,
    113 |     authenticated: state.auth.auth_token !== null,

Also, when I hovered over the <typeof rootReducer>, this is what I'm seeing

const rootReducer: Reducer<CombinedState<{
    dashboard: never;
    sales: never;
    auth: never;
}>, AppActions>

authReducer when I hover over it

(alias) const authReducer: (state: AuthInitialState | undefined, action: AuthActionTypes) => {} | undefined
import authReducer

AuthInitialState the types for initial states in auth reducer

// initialState for reducers
export interface AuthInitialState {
  readonly loading: boolean;
  readonly errorMessage: string | null;
  readonly successMessage: string | null;
  // user info
  readonly userInfoObj: TReceivedUserInfoObj | null;
  // auth token
  readonly auth_token: string | null;
}

AuthActionTypes A bunch of interfaces of actions union into one type

export type AuthActionTypes = 
  | SignInAction
  | SignInStartAction
  | SignInSucceedAction
  | SignInFailedAction
 
  | SignOutAction
 
  | GetUserInfoAction
  | GetUserInfoStartAction
  | GetUserInfoSucceedAction
  | GetUserInfoFailedAction;

example of sign in actions

export interface SignInAction {
  type: typeof actionTypes.SIGN_IN;
  email: string;
  password: string;
}

export interface SignInStartAction {
  type: typeof actionTypes.SIGN_IN_START;
}

export interface SignInSucceedAction {
  type: typeof actionTypes.SIGN_IN_SUCCEED;
  auth_token: string;
}

export interface SignInFailedAction {
  type: typeof actionTypes.SIGN_IN_FAILED;
  errorMessage: string;
}

In short, my problem here is unable to setup rootReducer smoothly with redux persist and causing states to be type never when extracting states through mapStateToProps

My apology if the post got a little too long and hard to read, thanks for all your patience and help in advance.


UPDATE I saw this post and implemented the Reducer<State, ActionType> thing. Now the type never error is gone, but new issue arises.

In my auth.ts reducer file,

const reducer: Reducer<AuthInitialState, AuthActionTypes> = (state = initialState, action) => {

It's now giving me this error

const reducer: Reducer<AuthInitialState, AuthActionTypes>
Type '(state: AuthInitialState | undefined, action: AuthActionTypes) => {} | undefined' is not assignable to type 'Reducer<AuthInitialState, AuthActionTypes>'.
  Type '{} | undefined' is not assignable to type 'AuthInitialState'.
    Type 'undefined' is not assignable to type 'AuthInitialState'.ts(2322)
1
Typescript should be smart enough to infer the correct typo. You could simpre remove : any and let typescript assign/infer the correct type automatically, - Rumpelstinsk
Could you please show us instead what you did and what error you have? As @Rumpelstinsk rightly pointed out, let typescript infer the correct type instead. - jperl
Added more details on what i did, I dont really know what I'm doing tbh with the redux types here, I just followed some tutorials on youtube and mimic the way they declare types for their mapStateToProps and mapDispatchToProps - Henry Choo

1 Answers

0
votes

Note that we do not have to explicitly declare a new interface for RootState. We can use ReturnType to infer state shape from the rootReducer.

const rootReducer = combineReducers({
  dashboard: dashboardReducer,
  sales: salesReducer,
  auth: authReducer,
});

export type RootState = ReturnType<typeof rootReducer>

checkout this official redux docs