使用 redux-actions 库键入 reducer

Typing reducer with redux-actions library

由于我使用 redux-actions 库中的 createAction,因此我正在努力输入我的 reducer 的第二个参数 action。

我的reducer部分代码如下

type AddItems = 'addItems';
const AddItems: AddItems = 'addItems';

interface AddItemAction extends Action<CartItem> {
    type: AddItems;
}

export const addItems = createAction<CartItem>(AddItems);

type RemoveItem = 'removeItem';
const RemoveItem: RemoveItem = 'removeItem';

interface RemoveItemAction extends Action<number> {
    type: RemoveItem;
}

export const removeItem = createAction<number>(RemoveItem);

export type ShoppingCartActions = AddItemAction | RemoveItemAction;

export const shoppingCartActions = {
    addItems,
    removeItem
};

export default function shoppingCartReducer(state: ShoppingCartStore = shoppingCartInitialState, action: ShoppingCartActions) {
    const { type, payload } = action;    
    switch (type) {
        case AddItems:
           return {
            cartItems: [
                ...state.cartItems.filter(cartItem => cartItem.item.id !== payload!.item.id),
                payload
            ]               
           };
        case RemoveItem:
           return {
               cartItems: [
                   ...state.cartItems.filter(cartItem => cartItem.item.id !== payload)
               ]
           };
        default:
            return state;
    }
}

我遇到的问题是,当我在我的减速器中访问 payload 上的 属性 时,它认为每个案例都必须存在 属性。例如,在 addItems 的情况下,我访问 payload.item 但打字稿抱怨,因为它也在寻找 item 是来自 RemoveItemAction.[=18= 的类型号]

免责声明:我没有在本地安装 redux 或 redux-actions 库,所以我不能 100% 确定这个答案是否正确;请测试一下。


ShoppingCartActionsdiscriminated union,其中 type 是判别式 属性。当您检查判别式时,您希望 TypeScript 像宣传的那样行事:它应该自动将 action 变量的类型缩小到联合的适当组成部分。

不幸的是,您正在将 action 解构为两个变量,typepayload,如下所示:

const { type, payload } = action;    

并且编译器不理解 typepayload 的类型是相关的。 也就是说,如果您缩小 typeAddItems | RemoveItemAddItems,编译器 而不是 会自动将 payloadCartItem | number 缩小到 CartItem。尽管我们知道一定是这样,但是编译器没有我们聪明,它用来分析控制流的heuristics也达不到要求像这样理解"entangled variables"。

你是not the first person to run into this issue。语言设计者在这些问题中的评论通常类似于 "it would be nice if the compiler could do this for you, but it would be very expensive to implement and lead to poor compiler performance".


在这种情况下,最简单的解决方法是按照 TypeScript 的意图使用区分联合:不要解构action。在 action 上使用直接 属性 访问:

export default function shoppingCartReducer(
  state: ShoppingCartStore = shoppingCartInitialState, 
  action: ShoppingCartActions
) {
  // switch on property of action
  switch (action.type) {
    case AddItems:
      return {
        // action.payload is now CartItem
        cartItems: [              
          ...state.cartItems.filter(
            cartItem => cartItem.item.id !== action.payload!.item.id
          ),
          action.payload
        ]
      };
    case RemoveItem:
      // action.payload is now number
      return {
        cartItems: [
          ...state.cartItems.filter(
            cartItem => cartItem.item.id !== action.payload
          )
        ]
      };
    default:
      return state;
  }
}

我认为这现在应该可以正常工作了。


希望对您有所帮助。祝你好运!