I am building an app in React Typescript and I have a state defined like so:
const [products, setProducts] = useState({
fruits: [],
veggies: [],
herbs: [],
});
When I want to add a product from an HTML select
, let's say a fruit, I will call the following in the onChange
function:
addProduct("fruits", value);
Where the function addProduct
looks something like below. I am using productType
as a key to access the specific product type in products
:
const addProduct = (productType: string, value: string) => {
setProducts((products) => ({
...products,
[productType]: [...products.productType, value],
}));
}
However, I am getting this error (screenshot attached to link):
TS2339: Property 'productType' does not exist on type '{ fruits: never[]; veggies: never[]; herbs: never[]; }'. where ...products.productType
is highlighted.
I've tried accessing ...products.productType
using the dollar sign identifier, template literals but to no avail. How can I access ...products.productType
? Is there a correct syntax to use, or is there a hacky solution around this?
EDIT: Thank you for the suggestions on defining the types, I will take that into consideration. However, I think this is the underlying issue:
"TS2339: Property 'productType' does not exist on type 'IState'".
If I try to access ...products.productType
, it won't let me because productType
does not exist in my products
state. How can I get it to search for the string literal (which, in this example, is "fruits")?
CodePudding user response:
I will recommend keeping 3 different states instead of using only one. That will be way more flexible and easy to use and understand (for you and others devs touching your code).
In case you still want to do it that way, what is happening to you is that typescript is inferring
the types from your initial values, which are empty arrays = never[]
. To fix this you should type your state like:
interface IState {
fruits: string[], // use whatever value it should be, I'm just guessing its a string
veggies: string[],
herbs: string[],
}
const [products, setProducts] = useState<IState>({
fruits: [],
veggies: [],
herbs: [],
});
const addProduct = (productType: keyof IState, value: string) => {
setProducts((products) => ({
...products,
[productType]: [...products[productType], value],
}));
}
EDIT: Note that there are 2 more errors
producType
should not be of type string but a key ofIState
- If you access an object with dot notation then TS expects the object to have that exact key. Instead, you should use bracket notation.
CodePudding user response:
There are some things wrong with your code like @super mentioned, you need to fix those as well. As for reason behind the issue you are getting is because you are trying to access the productType
property of the old state using products.productType
.
The property you are trying to access does not exists which is causing the issue and giving you the error.
To access the old states values to concat in value array change your code as below,
setProducts((products) => ({
...products,
[productType]: [...products[productType], value],
}));
This should fix your issue.
EDIT
After change in code to include types for the state object the change was needed is given by @toni's answer.