Given an object:
const obj = { a: 1 } as { a: number } | { b: number }
const key = "a" as string
if (key in obj) {
console.log(obj[key])
// string cannot be used to index { a: number } | { b: number }
}
How do I safely access obj
(without type assertion)?
I've can't use literal checks
if ("a" in obj) {
// obj.a is number
}
since the value of key
is not pre-determined.
The sort of narrowing you're trying to do with if (key in obj)
is unfortunately tricky to get right. See microsoft/TypeScript#34867 for an official discussion about it.
Currently TypeScript will cannot narrow key
when you check key in obj
; this is requested in microsoft/TypeScript#43284, but that would interact poorly with some of the existing in
-operator narrowing that acts on the type of obj
.
Even if key narrowing were implemented, it wouldn't work very well when obj
is of a union type. In some sense you're trying to narrow both key
and obj
at once, in some sort of correlated way, such that key in obj
implies that the pair [key, obj]
is of a type like ["a", {a: number}] | ["b", {b: number}]
.
But there is no facility in TypeScript to handle such things; see microsoft/TypeScript#30581 for the general problem with correlated unions; often these can be refactored to use generics instead, as described in microsoft/TypeScript#47109, but in this case it wouldn't be even remotely worth the effort.
Probably my recommendation here is that you should widen the type of obj
to something more amenable to indexing with an unknown key. Perhaps with an index signature like
const _obj: { [k: string]: number | undefined } = obj;
This represents the idea that any property will either be number
or missing/undefined
, and you can just index directly (without in
checking) and compare against undefined
:
const val = _obj[key];
if (typeof val !== "undefined") {
console.log(val)
}