React-Redux Typescript,reducer 操作错误的类型

时间:2021-07-19 10:51:31

标签: reactjs typescript redux

我正在将我的 react 应用程序转换为 Typescript,但遇到了 reducer 中操作类型的问题。

我在使用 Discriminated Unions 时遇到错误,例如在操作 ADD_TO_CART 上,当我在 Discriminated Unions 中有多个声明时,它会向我显示错误

Property 'product' does not exist on type 'ICart | IProduct | { products: IProduct[]; } | IOrder'.
  Property 'product' does not exist on type 'IProduct'.ts(2339)

当我删除所有其他操作类型并只保留 ADD_TO_CART 时,打字稿中的错误消失了。如何声明我的操作,使其不显示错误?

同样适用于其他类型,例如 GET_PRODUCTS,当我从 Discriminated Unions 中删除所有其他类型并只留下 GET_PRODUCTS 时,错误消失了,但是当我声明它显示的所有操作时:

Property 'products' does not exist on type 'ICart | IProduct | { products: IProduct[]; } | IOrder'.
  Property 'products' does not exist on type 'ICart'.ts(2339)

我的类型 - 声明文件

export interface IArticle {
  _id: string,
  title: string,
  shortDescription: string,
  content: string,
  author: string,
  createdAt: Date,
  updatedAt: Date,
}

export interface IProduct {
  _id: string,
  title: string,
  category: string,
  shortDescription: string,
  description: string,
  photo: {
    fileName: string,
    url: string,
  },
  createdAt: Date,
  updatedAt: Date,
}

export interface ICart {
  product: IProduct,
  qty: number,
}

export interface IOrder {
  nr: number,
  products: object,
  user: string | IUser,
  status: string,
  comment: string,
  createdAt: Date,
  updatedAt: Date,
}

我的减速机

import {
  ADD_TO_CART, GET_PRODUCTS, DELETE_FROM_CART, ADD_ORDER, GET_ORDERS, GET_ONE_ORDER,
  CLEAR_ONE_ORDER, ARCHIVE_ORDER, GET_ARCHIVED_ORDERS, GET_ONE_ARCHIVED_ORDER,
  CLEAR_ONE_ARCHIVED_ORDER, GET_ORDERS_ADMIN, START_ORDER, GET_RECAP_ADMIN,
} from '../actions/types.action';
import { IProduct, IOrder, ICart } from '../declarations';

const initialState = {
  products: [],
  isLoading: true,
  cart: {},
  orders: [],
  adminOrders: [],
  recapLoading: true,
  recapAdmin: null,
  archivedOrders: [],
  archivedOrdersLoading: true,
  oneArchivedOrder: null,
  oneArchivedOrderLoading: true,
  cartLength: null,
  oneOrder: null,
  oneOrderLoading: true,
  backdropOpen: false,
};

type ACTIONTYPE =
| { type: 'ADD_TO_CART'; payload: ICart }
| { type: 'GET_PRODUCTS'; payload: { products: IProduct[] } }
| { type: 'DELETE_FROM_CART'; payload: IProduct }
| { type: 'ADD_ORDER'; payload: null }
| { type: 'GET_ORDERS'; payload: null }
| { type: 'GET_ONE_ORDER'; payload: null }
| { type: 'CLEAR_ONE_ORDER'; payload: null }
| { type: 'ARCHIVE_ORDER'; payload: IOrder }
| { type: 'GET_ARCHIVED_ORDERS'; payload: null }
| { type: 'GET_ONE_ARCHIVED_ORDER'; payload: null }
| { type: 'CLEAR_ONE_ARCHIVED_ORDER'; payload: null }
| { type: 'GET_ORDERS_ADMIN'; payload: null }
| { type: 'START_ORDER'; payload: null }
| { type: 'GET_RECAP_ADMIN'; payload: null };

export default function (state = initialState, action: ACTIONTYPE) {
  const { type, payload } = action;

  switch (type) {
    case ADD_TO_CART:
      return {
        ...state,
        cart: {
          ...state.cart,
          [payload?.product?._id]: {
            product: payload.product,
            qty: payload.qty,
          },
        },
      };
    case DELETE_FROM_CART:
      return {
        ...state,
        cart: update(state.cart, {
          $unset: [payload],
        }),
      };
    case GET_PRODUCTS:
      return {
        ...state,
        products: payload.products,
        isLoading: false,
      };
    case GET_RECAP_ADMIN:
      return {
        ...state,
        recapAdmin: payload,
        recapLoading: false,
      };
    case ADD_ORDER:
      return {
        ...state,
        cart: {},
        backdropOpen: false,
      };
    case START_ORDER:
      return {
        ...state,
        backdropOpen: true,
      };
    case GET_ORDERS:
      return {
        ...state,
        orders: payload,
        isLoading: false,
      };
    case GET_ONE_ORDER:
      return {
        ...state,
        oneOrder: payload,
        oneOrderLoading: false,
      };
    case CLEAR_ONE_ORDER:
      return {
        ...state,
        oneOrder: null,
        oneOrderLoading: true,
      };
    case ARCHIVE_ORDER:
      return {
        ...state,
        orders: state.orders.filter((o) => o._id !== payload?._id),
        isLoading: false,
      };
    case GET_ARCHIVED_ORDERS:
      return {
        ...state,
        archivedOrders: payload,
        archivedOrdersLoading: false,
      };
    case GET_ONE_ARCHIVED_ORDER:
      return {
        ...state,
        oneArchivedOrder: payload,
        oneArchivedOrderLoading: false,
      };
    case CLEAR_ONE_ARCHIVED_ORDER:
      return {
        ...state,
        oneArchivedOrder: null,
        oneArchivedOrderLoading: true,
      };
    case GET_ORDERS_ADMIN:
      return {
        ...state,
        adminOrders: payload,
        isLoading: false,
      };
    default:
      return state;
  }
}

1 个答案:

答案 0 :(得分:2)

我们特别推荐not trying to create TS union types of your Redux actions。此外,您不必手动编写任何单独的动作创建者或动作类型,因为 our official Redux Toolkit package does all that work for you automatically

因此,在这种情况下,最好的方法是使用 Redux Toolkit 的 createSlice API 重写此逻辑,这将避免类型问题、简化逻辑并消除对所有这些手写操作的需要。