Reputation: 167
In this example I have a cart, which can be filled with different kinds of items, in this case its golfballs and golfclubs, which have their own options.
I get the following error with the code below:
TS2339: Property 'color' does not exist on type '{ color: "blue" | "red" | "white"; } | { variant: "wedge" | "putter"; }'.
Property 'color' does not exist on type '{ variant: "wedge" | "putter"; }'.
type ProductGolfBall = {
type: "golfball";
options: {
color: "blue" | "red" | "white";
};
};
type ProductGolfClub = {
type: "golfclub";
options: {
variant: "wedge" | "putter";
};
};
type CartItem = ProductGolfBall | ProductGolfClub;
type CartItems = Array<CartItem>;
const cart: CartItems = [
{
type: "golfball",
options: {
color: "blue"
}
},
{
type: "golfclub",
options: {
variant: "wedge"
}
},
{
type: "golfclub",
options: {
variant: "putter"
}
}
];
const golfball = cart.find((item) => item.type === "golfball");
if (golfball) { // Check that it's truthy
// According to typescript this can still be either ProductGolfClub or ProductGolfBall
console.log(golfball.type)
console.log(golfball.options.color) // Produces the TS2339 error above
}
Now I just don't see why the golfball
variable can still be ProductGolfClub
when the find operation only returns true for an array item where the type
property is golfball
.
I could just cast set the golfball
variable as ProductGolfBall
, but there must be some other way to have typescript understand what type the variable has.
Upvotes: 5
Views: 1333
Reputation: 20132
Unfortunetly TS is not able to make type guard from this case out of box. But you can make it manually by saying explicitly what the function will ensure, consider:
function isGolfBall(item: CartItem): item is ProductGolfBall {
return item.type === "golfball";
}
const golfball = cart.find(isGolfBall)
if (golfball) { // Check that it's truthy
console.log(golfball.type)
console.log(golfball.options.color) // no error 👌
}
Most important is :item is ProductGolfBall
it means that we explicitly say this function will be a type guard which will pass (return true) only for ProductGolfBall
.
Inline solution will do also:
const golfball = cart.find((item): item is ProductGolfBall => item.type === "golfball")
Upvotes: 7