EDIT: The problem was simply not using strict mode, which correctly disallowed the types with null
properties and correctly inferred both true and false cases.
I'm writing a discriminated union where the tag is a boolean, but trying to use !x.tag
as a type guard doesn't work. I suspected the reason is because a falsey value like null
or undefined
would also count, but defining the tag type as NonNullable<true>
and NonNullable<false>
didn't help -- I'm still able to create an object of type isTrue
or isFalse
with tag: null
. This would make it fail the type guard even in the truthy case, so I don't understand what I'm missing. Example code:
// definitions:
interface isTrue {
tag: NonNullable<true>;
data: string;
}
interface isFalse {
tag: NonNullable<false>;
data: number;
}
type either = isTrue | isFalse;
// playing around:
const falsy: isFalse = { tag: null, data: 5 }; // would expect this not to be ok but it is
const truthy: isTrue = { tag: null, data: 'a' }; // this is also allowed
decider({ tag: null, data: 4 }) // a legal call since the argument is valid as a isFalse object.
function decider(x: either) {
if (x.tag) { // inferred as isTrue
x.data
x.tag
} else { // still a union
x.data
x.tag
}
}
I've also tried putting !x.tag
in the if
clause and leaving the true
case to the else
clause, but in both cases the true
clause is inferred while the false
one isn't.