Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Discriminatory unions in an object, don't reflect type, when access through loops – Typescript

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

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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
    }
})

Playground link to code

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading