Issue
TFlavour is a discriminatory union, which is then a value of an Object.
Trying to implement this, works in js, but ts gets angry. ts playground link
Expected: ts to understand discriminated unions in loops, as it understands it without loops
type TFlavour = ({
natural: true,
naturalComponent : string
}) | ({
natural: false,
artificialComponent: string
})
type TIceCream = Record<string, TFlavour>
const IceCreams: TIceCream = {
CokeIceCream: {
natural:false,
artificialComponent: 'Coke'
},
Vanilla: {
natural: true,
naturalComponent: 'Vanilla Extract'
},
Mango: {
natural: true,
naturalComponent: 'Mango Fruit'
}
}
const iceCreamKeys = Object.keys(IceCreams)
iceCreamKeys.forEach( item => {
if(IceCreams[item].natural){
console.log(IceCreams[item].naturalComponent) // ts says "Property doesn't exists.."
}
})
if(IceCreams.Mango.natural){
console.log(IceCreams.Mango.naturalComponent) // Works here
}
Solution
The problem is that the compiler doesn't know how to do narrowing on an object property like IceCreams[item] where you are indexing with a key whose type isn't known to be a specific literal type. TypeScript is only following the type of the index, not the identity. And the type of item is string. If you have item1 and item2, both of type string, then checking IceCreams[item1] wouldn't let you conclude anything about IceCreams[item2], right? And since TypeScript can't tell the difference between item1 vs item2 or item vs item, it can't narrow. This is a known limitation of TypeScript reported at microsoft/TypeScript#10530. Maybe someday it will be addressed. But for now, there's an easy workaround:
Just copy the value into a new variable, so that the problematic indexing occurs only once:
iceCreamKeys.forEach(item => {
const x = IceCreams[item];
if (x.natural) {
console.log(x.naturalComponent) // okay
}
})
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.