Reputation: 570
This is a follow up question to Typescript property does not exist on union type.
The solution provided is to evaluate either object in the union by using the in operator. Which works great! Here's the example:
type Obj1 = { message: string }
type Obj2 = { text: string }
const getText = (obj: Obj1 | Obj2): string => {
if ("message" in obj) {
return obj.message
}
return obj.text
}
However, when I try to evaluate it with a variable the same error occurs:
const getText = (obj: Obj1 | Obj2): string => {
let property = "message"
if (property in obj) {
return obj.message
}
return obj.text
}
Am I just missing something really obvious?
Upvotes: 1
Views: 755
Reputation: 187004
Typescript does not believe that a conditional like this actually narrows the type when the key is a variable (as opposed to a literal). You can tell this because when you hover over obj in the conditional branch it reports a type of Obj1 | Obj2
.
It does seem like the compiler should be able to figure this out and know it's safe, but the typescript compiler isn't perfect. But it's designed to err on the side of pessimism because over reporting issues is much less dangerous than allowing issues to slip into production code.
This case may seem like a big limitation. but in practice, it's not going to come up much. Your code requires the message
property to be available in that conditional. Using a variable to test for that doesn't make any sense because in order to have type safe code that could only ever be one value for property
. So you may as well use just use a literal string in the test.
But if you test this a lot and you want to avoid typing the sam literal over and over, you could make a simple reusable type guard function to do this:
function isObj1(obj: Obj1 | Obj2): obj is Obj1 {
return 'message' in obj
}
const getText = (obj: Obj1 | Obj2): string => {
if (isObj1(obj)) {
return obj.message
}
return obj.text
}
Upvotes: 1