Reputation: 317
I'm transforming my react app to Typescript and I've run into the problem with types of actions in the reducer.
I'm having an error when I use Discriminated Unions, for example on the action ADD_TO_CART, when I have multiple declarations in Discriminated Unions it shows me an error
Property 'product' does not exist on type 'ICart | IProduct | { products: IProduct[]; } | IOrder'.
Property 'product' does not exist on type 'IProduct'.ts(2339)
When I delete all the other action types and only leave ADD_TO_CART the error is gone from typescript. How can I declare my actions so it doesn't show an error?
the same goes for other types for example GET_PRODUCTS when I delete all the other types from Discriminated Unions and only leave GET_PRODUCTS the error is gone but when I declare all the actions it shows:
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,
}
MY REDUCER
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;
}
}
Upvotes: 0
Views: 717
Reputation: 67439
We specifically recommend not trying to create TS union types of your Redux actions. Also, you shouldn't have to write any separate action creators or action types by hand, because our official Redux Toolkit package does all that work for you automatically.
So, in this case the best approach is to rewrite this logic using Redux Toolkit's createSlice
API, which will both avoid the type issues, simplify the logic, and remove the need for all those hand-written actions.
Upvotes: 2