In the code below, why is typeof data.x === 'string'
type guard insufficient to distinguish the union type?
interface A { x: string, y: number }
interface B { x: number, y: string }
function handler(data: A | B) {
if (typeof data.x === 'string') {
data.y // string | number --- WHUT?
}
}
In what case could handler
be called with the (invalid) shape { x: string: y: string }
?
Using a discriminant, it works (why?):
interface A { kind: 'A', x: string, y: number }
interface B { kind: 'B', x: number, y: string }
function handler(data: A | B) {
if (data.kind === 'A') {
data.y // number
}
}
Reading offic docs on Discriminated Unions doesn't help.
They only state:
Some languages automatically discriminate unions for you; TypeScript instead builds on JavaScript patterns as they exist today.
That doesn't explain why TS can work with similar type guards in some cases, while not in the case of a union (I don't see any ambiguity in my first example).
Discriminated unions need to have a very particular structure. I once dug up the rules here:
Narrowing the parent object is only done in specific scenarios, when the property is considered a discriminant for the union. A property is considered as a discriminant property if:
If those rules are not followed you just end up with a field discrimination not a parent object discrimination.