【问题标题】:Typing reducer payload as an object type or array of such将 reducer 有效负载键入为对象类型或此类的数组
【发布时间】:2021-06-25 01:47:54
【问题描述】:

我正在使用 useReducer 来维护对象类型数组的状态,并且我有一些操作类型。界面如下:

interface MyObject {
  id: string;
  foo: string;
  bar: string;
}

interface Action {
  type: 'add' | 'remove' | 'update';
  payload?: Partial<MyObject>;
}

reducer 函数:

const reducer = (current: MyObject[], action: Action): MyObject[] => {
  switch (action.type) {
    case 'add':
      return [...current, createBaseMyObject()];
    case 'remove':
      return current.filter((item) => item.id !== action.payload?.id);
    case 'update':
      return current.map((item) => {
        if (item.id === action.payload?.id) {
          return { ...item, ...action.payload };
        }
        return item;
      });

    default:
      return current;
  }
};

这没问题,但我还需要有条件地将 MyObject 数组作为有效负载传递以进行批量更新。尝试将类型和功能更改为:

interface ReducerAction {
  type: 'add' | 'remove' | 'update';
  payload?: Partial<MyObject> | MyObject[];
}

const reducer = (current: MyObject[], action: Action): MyObject[] => {
  switch (action.type) {
    case 'add':
      return [...current, createBaseMyObject()];
    case 'remove':
      // Property 'id' does not exist on type 'MyObject[]'
      return current.filter((item) => item.id !== action.payload?.id);
    case 'update':
      // With proper typing maybe this should be dedicated case
      if (Array.isArray(action.payload)) {
        return action.payload;
      }

      return current.map((item) => {
        // Property 'id' does not exist on type 'MyObject[]'
        if (item.id === action.payload?.id) {
          return { ...item, ...action.payload };
        }
        return item;
      });

    default:
      return current;
  }
};

我试图避免断言或额外的运行时检查只是为了满足 TS。如果创建一个专用案例更容易,那很好。我只想尽可能最好地输入它,最好保持简单。想法?

【问题讨论】:

    标签: typescript react-typescript use-reducer


    【解决方案1】:

    如果您希望 Typescript 了解某些类型具有特定的有效负载,那么您需要一个联合类型。我会将'update' 的两个用例分开。 'replace' 似乎是数组有效负载的更好名称,因为您正在用该数组替换整个状态。

    type Action = {
      type: 'add';
      // no payload 
    } | {
      type: 'remove' | 'update';
      // partial but with id required
      payload: Partial<MyObject> & Pick<MyObject, 'id'>;
    } | {
      type: 'replace';
      // array of objects
      payload: MyObject[];
    }
    
    const reducer = (current: MyObject[], action: Action): MyObject[] => {
      switch (action.type) {
        case "add":
          return [...current, createBaseMyObject()];
        case "remove":
          return current.filter((item) => item.id !== action.payload.id);
        case "update":
          return current.map((item) => {
            if (item.id === action.payload.id) {
              return { ...item, ...action.payload };
            }
            return item;
          });
        case "replace":
          return action.payload;
        default:
          return current;
      }
    };
    

    【讨论】:

    • 非常简单的解决方案,谢谢!真的很喜欢“替换”作为语义案例。 ?
    猜你喜欢
    • 2018-06-11
    • 2018-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多