I have the following TypeScript error when using useState Argument of type '(previousState: IsProducts) => { list: IsProduct[]; }' is not assignable to parameter of type 'IsProducts'.
I’m not too sure what the issue is with the type. If anyone can point me in the right direction it would be appreciated 🙂
export interface IsProduct {
name: string;
price: number;
quantity: number;
}
export interface IsProducts {
list: IsProduct[];
}
const [products, setProducts] = useState<IsProducts | undefined>({
list: [
{
name: 'Test One',
price: 20,
quantity: 2,
},
{
name: 'Test Two',
price: 10,
quantity: 1,
},
],
});
setProducts((previousState: IsProducts) => {
return {
...previousState,
list: previousState.list.map((product: IsProduct, idx: number) => {
if (idx === index) {
return {
...product,
quantity: product.quantity >= 10 ? (product.quantity = 10) : product.quantity + 1,
};
}
return product;
}),
};
});
Removing setProducts
from IsProductContext
removes the above error, but throws a different error on context.
export interface IsProductContext {
products: IsProducts;
setProducts: (products: IsProducts) => void;
}
2
Answers
When you invoke useState you parametrized it with IsProducts | undefined, and then when you call setProducts you have previousState: IsProducts. You have mismatch types.
Expanding on a comment. You create
setProducts
withuseState
:At this point,
setProducts
has this somewhat opaque and unhelpful type:Inspecting the React typings you can confirm that these are aliases for more familiar constructs:
No surprises – we can call
setProducts(..)
either with anIsProducts | undefined
value, or a function that returns anIsProducts | undefined
value.Your context type is:
setProducts
in the context has a different type fromsetProducts
as returned byuseState
–Âcontext.setProducts
only advertises that it accepts anIsProducts
value, not the callback form. When you supply it with a callback value, Typescript rightly reports an error.If you update the context type to match React’s typings for the setter returned by
useState
…… then you’re all good.
As a side-note, in my projects I generally set up a couple of aliases for these type of state update functions, to decouple them from React more than anything else:
in which case you would have
setProducts: StateSetter<IsProducts | undefined>
in your context.