Why can't I use property checking to narrow the type like so?
function test2(value:{a:number}|{b:number}){
// `.a` underlined with: "Property a does not exist on type {b:number}"
if(value.a != null){
console.log('value of a:',value.a)
}
}
test2({a:1})
test2({b:2})
It transpiles fine, works as designed, and throws no runtime errors. The property check logically narrows the scope to {a:number}
.
So what's wrong with this? Is there a setting I can change to make it allow and use this method of type narrowing?
Someone will probably tell me to use the in
operator. So I suppose I'll say now that I know I could use the in
operator, but I don't want to. If I union the type with string as well, then I can't use in
with string and I have to first check to make sure it's not a string with if(typeof value != 'string')
, but in regular JavasScript I could simply leave my property check and know that whatever I get will have the property and all will be well.
So, what's the problem with TypeScript here? Is it just not smart enough to figure it out?
(Changes: This question was originally about optional chaining to check for properties because the type was unioned with a null as well, but to simplify the question I've removed the null and the optional chain operator from the if statement.)
what's the problem with TypeScript here?
It just points out diligently that your code is accessing a property that is not declared to exist on the object. If it wasn't a union type, you'd expect to get this error message. That the code doesn't throw a runtime error doesn't mean that it's valid TypeScript.
If you insist on accessing the property to discriminate the union, you'll have to declare it on all type constituents - you still can declare it to have no value on the others:
function test2(value: { a: number; b?: never } | { b: number; a?: never }){
if(value.a != null) {
console.log('value of a:', value.a)
}
}
test2({a:1})
test2({b:2})