Redux + Typescript: типы параметров 'action' и 'action' несовместимы - PullRequest
0 голосов
/ 20 декабря 2018

У меня есть досадный случай, когда я не могу понять, почему TS выдает следующую ошибку:

src/store.ts:24:3 - error TS2322: Type 'Reducer<MemberState, InvalidateMembers>' is not assignable to type 'Reducer<MemberState, RootActions>'.
  Types of parameters 'action' and 'action' are incompatible.
    Type 'RootActions' is not assignable to type 'InvalidateMembers'.
      Type 'InvalidateCatgories' is not assignable to type 'InvalidateMembers'.

24   member,
     ~~~~~~

  src/store.ts:18:3
    18   member: MemberState;
         ~~~~~~
    The expected type comes from property 'member' which is declared here on type 'ReducersMapObject<RootState, RootActions>'

src/store.ts:25:3 - error TS2322: Type 'Reducer<CategoryState, InvalidateCatgories>' is not assignable to type 'Reducer<CategoryState, RootActions>'.
  Types of parameters 'action' and 'action' are incompatible.
    Type 'RootActions' is not assignable to type 'InvalidateCatgories'.
      Type 'InvalidateMembers' is not assignable to type 'InvalidateCatgories'.

25   category,
     ~~~~~~~~

  src/store.ts:19:3
    19   category: CategoryState;
         ~~~~~~~~
    The expected type comes from property 'category' which is declared here on type 'ReducersMapObject<RootState, RootActions>'

Почему он пытается назначить один интерфейс другому (InvalidateMembers - InvalidateCatgories инаоборот)?Единственный способ избавиться от ошибки - это изменить тип type на string (чтобы два интерфейса имели одинаковую структуру) в таких интерфейсах, как:

interface InvalidateMembers extends Action {
  type: string;
}

Это меня так озадачивает.Я уже все трижды проверил + проверил все типы редуксов, но не могу понять причину ошибки.

- ОБНОВЛЕНИЕ: -

После более детальной проверки типов редуксов я понял, чтоReducersMapObject возвращает каждое свойство rootReducer по всему объекту RootActions как единое целое, которое, очевидно, больше не будет соответствовать одному свойству.Я думаю, что это больше вопрос дизайна самого типа, или?

export type Reducer<S = any, A extends Action = AnyAction> = (
  state: S | undefined,
  action: A
) => S

/**
 * Object whose values correspond to different reducer functions.
 *
 * @template A The type of actions the reducers can potentially respond to.
 */
export type ReducersMapObject<S = any, A extends Action = Action> = {
  [K in keyof S]: Reducer<S[K], A>
}

Буду очень признателен за ваш отзыв.

store.js

...

export interface RootState {
  member: MemberState;
  category: CategoryState;
}
export type RootActions = MemberAction | CategoryAction;

const rootReducer = combineReducers<RootState, RootActions>({
  member,
  category,
});

export const store = createStore(
  rootReducer,
  composeWithDevTools(applyMiddleware(thunk as ThunkMiddleware<RootState, RootActions>))
);

actions / member.js

export enum MemberActionTypes {
  INVALIDATE_MEMBERS = 'INVALIDATE_MEMBERS'
}

interface InvalidateMembers extends Action {
  type: MemberActionTypes.INVALIDATE_MEMBERS;
}

export const invalidateMembers = (): ThunkResult<void> => (dispatch) => {
  dispatch({
    type: MemberActionTypes.INVALIDATE_MEMBERS
  });
};

export type MemberAction = InvalidateMembers;

actions / category.js

export enum CategoryActionTypes {
  INVALIDATE_CATEGORIES = 'INVALIDATE_CATEGORIES'
}

interface InvalidateCatgories extends Action {
  type: CategoryActionTypes.INVALIDATE_CATEGORIES;
}

export const invalidateCategories = (): ThunkResult<void> => (dispatch) => {
  dispatch({
    type: CategoryActionTypes.INVALIDATE_CATEGORIES
  });
};

export type CategoryAction = InvalidateCatgories;

redurs / member.js

export interface MemberState {
  items: {};
}

const initialState = {
  items: {}
};

export const member: Reducer<MemberState, MemberAction> = (state = initialState, action) => {
  switch (action.type) {
    case MemberActionTypes.INVALIDATE_MEMBERS:
      return {
        ...state,
        didInvalidate: true
      };
    default:
      return state;
  }
};

redurs / category.js

export interface CategoryState {
  items: {};
}

const initialState = {
  items: {},
};

export const category: Reducer<CategoryState, CategoryAction> = (state = initialState, action) => {
  switch (action.type) {
    case CategoryActionTypes.INVALIDATE_CATEGORIES:
      return {
        ...state,
        didInvalidate: true
      };
    default:
      return state;
  }
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...