ค้นหาเว็บไซต์

javascript - ประเภทการส่งค่ากลับของ Redux actions ใน typescript ไม่ทำงาน


ดังนั้นพื้นฐานแล้วฉันกำลังพยายามส่งค่าประเภทสำหรับชุดฟังก์ชันและสามารถจับคู่นั้นในรีดิวเซอร์ของ Redux ของฉันได้

นี่คือความพยายามของฉัน:

actions.ts

export const SET_CART_ITEMS = "SET_CART_ITEMS";
export const SET_CART_TOTALS = "SET_CART_TOTALS";

export const ADD_CART_ITEM = "ADD_CART_ITEM";
export const REMOVE_CART_ITEM = "REMOVE_CART_ITEM";

export const SET_CART_LOADING = "SET_CART_LOADING";

export function setCart(cart: Cart) {
  return {
    type: SET_CART_ITEMS,
    cart
  } as const;
}

export function setTotals(totals: CartTotal) {
  return {
    type: SET_CART_TOTALS,
    totals
  }
}

export function addCartItem(cart: Cart) {
  return {
    type: ADD_CART_ITEM,
    cart
  } as const;
}

export function removeCartItem(cart: Cart) {
  return {
    type: REMOVE_CART_ITEM,
    cart
  } as const;
}

export function toggleCartLoading(loading: boolean) {
  return {
    type: SET_CART_LOADING,
    loading
  } as const;
}

export type CartActions = ReturnType<typeof setCart>| ReturnType<typeof addCartItem> | ReturnType<typeof removeCartItem> | ReturnType<typeof toggleCartLoading> | ReturnType<typeof setTotals>

reducer.ts

import { 
   CartActions,  
   ADD_CART_ITEM,
   REMOVE_CART_ITEM,
   SET_CART_ITEMS,
   SET_CART_TOTALS,
   SET_CART_LOADING 
} from '../../../actions';

export default (state: CartState = initialState, action: CartActions) : CartState => {
  switch (action.type) {
    case ADD_CART_ITEM:
      return {
        ...state,
        cart: {
          ...action.cart
        }
      }
    case REMOVE_CART_ITEM:
      return {
        ...state,
        cart: {
          ...action.cart
        }
      }
    case SET_CART_ITEMS:
      return {
        ...state,
        cart: action.cart
      }
    case SET_CART_TOTALS:
      return {
        ...state,
        totals: action.totals
      }
    case SET_CART_LOADING:
      return {
        ...state,
        loading: action.loading
      }
    default:
      break;
  }
  return state;
}

สิ่งนี้ใช้ไม่ได้เลย ข้อผิดพลาดที่ฉันได้รับเกิดขึ้นในสวิตช์ของ reducer โดยบอกว่า:

"Property 'cart' does not exist on type 'CartActions'.
  Property 'cart' does not exist on type '{ type: string; totals: CartTotal; }'."


Property 'loading' does not exist on type '{ readonly type: "SET_CART_ITEMS"; readonly cart: Record<string, CartItem>; } | { readonly type: "ADD_CART_ITEM"; readonly cart: Record<string, CartItem>; } | { ...; } | { ...; } | { ...; }'.
  Property 'loading' does not exist on type '{ readonly type: "SET_CART_ITEMS"; readonly cart: Record<string, CartItem>; }'.ts(2339)

ฉันพลาดอะไรที่ชัดเจนไปหรือเปล่า? ฉันอยากที่จะไม่ต้องพิมพ์การกระทำทั้งหมดโดยอินเทอร์เฟซด้วยตัวเองจริงๆ

แรงบันดาลใจจาก https://gist.github.com/schettino/c8bf5062ef99993ce32514807ffae849

คำตอบ #1

จำเป็นที่ชนิดการคืนค่าของตัวสร้างแอคชันแต่ละตัวจะต้องถูกระบุด้วย as const เพื่อที่ TypeScript จะสามารถแยกความแตกต่างของแอคชันของคุณในคำสั่ง switch ของ reducer ได้

ใน setTotals ดูเหมือนว่าจะขาดการยืนยัน const ไป - อย่างน้อยโค้ดที่ให้มาทำงานได้อีกครั้งใน playground หลังจากที่เพิ่มมันเข้าไปแล้ว

คุณสามารถสร้างตัวสร้างแอ็กชันทั่วไปเพื่อทำให้มันสามารถขยายได้มากขึ้นและละเว้น as const:

function createAction<T extends string, U, K extends string>(type: T, key: K, payload: U) {
  return ({
    type,
    [key]: payload
  }) as { type: T; } & { [P in K]: U }
  // need to cast, so TS doesn't return a widened index signature type
}

export function setTotals(totals: CartTotal) {
  return createAction(SET_CART_TOTALS, "totals", totals)
}
// same with the other functions

ตัวอย่างโค้ดใน Playground

คำตอบ #2

ด้วยวิธีที่คุณนำ CartActions ไปใช้งาน คุณควรกำหนดประเภทของแต่ละการกระทำให้กับมันเสมอ

ตัวอย่างเช่น คุณควรเปลี่ยน:

case REMOVE_CART_ITEM:
  return {
    ...state,
    cart: {
      ...action.cart
    }
  }

ไปยังนี้:

case REMOVE_CART_ITEM:
  return {
    ...state,
    cart: {
      ...action.cart
    }
  } as ReturnType<typeof removeCartItem>

หรือคุณสามารถแก้ไข CartActions ของคุณเพื่อให้ type ทำหน้าที่เป็นตัวคั่น:

const enum ActionTypes {
  SET_CART_ITEMS,
  SET_CART_TOTALS,
  ADD_CART_ITEM,
  REMOVE_CART_ITEM,
  SET_CART_LOADING
}



type CartActions =
  | {
      type:
        | ActionTypes.SET_CART_ITEMS
        | ActionTypes.ADD_CART_ITEM
        | ActionTypes.REMOVE_CART_ITEM;
      cart: Cart;
    }
  | {
      type: ActionTypes.SET_CART_TOTALS;
      totals: CartTotal;
    }
  | {
      type: ActionTypes.SET_CART_LOADING;
      loading: boolean;
    };

export function setCart(cart: Cart): CartActions {
  return {
    type: ActionTypes.SET_CART_ITEMS,
    cart
  };
}

export function setTotals(totals: CartTotal): CartActions {
  return {
    type: ActionTypes.SET_CART_TOTALS,
    totals
  };
}

export function addCartItem(cart: Cart): CartActions {
  return {
    type: ActionTypes.ADD_CART_ITEM,
    cart
  };
}

export function removeCartItem(cart: Cart): CartActions {
  return {
    type: ActionTypes.REMOVE_CART_ITEM,
    cart
  };
}

export function toggleCartLoading(loading: boolean): CartActions {
  return {
    type: ActionTypes.SET_CART_LOADING,
    loading
  };
}