So basicly I am trying to return types for a set of functions & being able to match that in my redux reducer.
Here is my attempt:
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;
}
This does not work at all, the error i'm getting is in the reducer switch saying:
"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)
Am I missing something obvious? I would really want to not needing to type all the actions by interfaces manually.
Inspiration from https://gist.github.com/schettino/c8bf5062ef99993ce32514807ffae849
It is necessary, that each action creator return type is annotated with as const, so TS can discriminate your actions in the switch statement of the reducer.
In setTotals this const assertion seems to be missing - at least the provided code works again in the playground, after having added it.
You could create a generic action creator to make it more scalable and omit 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
Code sample in Playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With