typescriptreduxredux-actions

Reducer Custom Dispatch Type, is not assignable, Types of property ‘dispatch’ are incompatible


I have a simple reducer & action types right now. While setting up my tests, I am running into a type def issue with the return of my store in reference to my "custom" action type declarations. I am seeing an error:

Type ‘Store<CombinedState<{ global: GlobalStateShape; }>, SetSelectedAccount >’ is not assignable to type ‘Store<any, AnyAction>’. Types of property ‘dispatch’ are incompatible

I have followed the Redux Typescript docs correctly AFAIK.

Why is SetSelectedAccount being returned when I invoke combineReducers()? The globalReducer is handling action of SetSelectedAccount.

In my test setup File where the error above occurs:

export function createTestStore(isInternal: boolean): Store {
  const store: Store<CombinedState<{
    global: GlobalStateShape;
    }>, SetSelectedAccount> = createStore(rootReducer, { global: 
     getGlobalInitialState(isInternal) });
  return store;
}

src/reducer.ts

export const rootReducer: Reducer<CombinedState<{
    global: GlobalStateShape;
}>, SetSelectedAccount> = combineReducers({
  global: globalReducer,
});

src/store.ts

const composedEnhancer = composeWithDevTools(
  applyMiddleware()
);

export const store: Store<CombinedState<{
  global: GlobalStateShape;
}>, SetSelectedAccount> = createStore(rootReducer, composedEnhancer);

global_reducer.ts:

export const globalReducer = (
  state = GLOBAL_INITIAL_STATE,
  action: GlobalActionTypes
): GlobalStateShape =>
  produce(state, draft => {
    switch (action.type) {
      case GlobalActions.SET_SELECTED_ACCOUNT: {
        draft.selectedAccountId = action.payload.accountId;
        break;
      }
    }
  });

actions.ts

const SET_SELECTED_ACCOUNT = 'SET_SELECTED_ACCOUNT';

export const GlobalActions = {
  SET_SELECTED_ACCOUNT,
};

export interface SetSelectedAccount {
  type: typeof SET_SELECTED_ACCOUNT;
  payload: { accountId: string; selectedApp: AppsList };
}

export type GlobalActionTypes = SetSelectedAccount;

FWIW, if I add a 2nd Action, then SetSelectedAccount turns into returned type above of GlobalActionTypes which is the same result & error message about mismatching dispatch

Thank you!


Solution

  • Problem

    The problem is the return type on your createTestStore function. You creating a store a a specific type (Store<CombinedState<{ global: GlobalStateShape; }>, SetSelectedAccount >) and then you are returning it as just Store with no generics. The default type for Store is the Store<any, AnyAction> which you see in your error message.

    You might think that this wouldn't be a problem because your store is more specific, so it should be assignable to Store, right? But specificity gets backwards when you are dealing with function arguments like those of Dispatch. The return type says that you are returning a store which is capable of dispatching AnyAction, but in reality you are only capable of dispatching the type SetSelectedAccount, so your store is not assignable to the broader type.

    Solutions

    The easiest thing to do is just remove that return type : State entirely. You've already typed the store variable which you are returning, so you don't really need it.

    You could also move the type annotation from the variable store to the function return. FYI the redux CombinedState utility type doesn't really do anything, CombinedState<{ global: GlobalStateShape; }> is the same as { global: GlobalStateShape; }

    export function createTestStore(isInternal: boolean): Store<{ global: GlobalStateShape; }, SetSelectedAccount> {
        return createStore(rootReducer, {
            global: getGlobalInitialState(isInternal)
        });
    }