How to check if a strongly typed object contains a given key in TypeScript without it complaining?
Take this code:
const lookup = {
foo: 1,
bar: 2
}
const getValueOrDefault = (name: string, defaultValue: number) => {
if (name in lookup) {
return lookup[name] // ERROR
}
return defaultValue
}
The lookup[name]
expression causes this TS error (here):
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ foo: number; bar: number; }'.
No index signature with a parameter of type 'string' was found on type '{ foo: number; bar: number; }'.
...even though I did if (name in lookup)
to check it first. I tried hasOwnProperty
, and that doesn't work either.
TypeScript is often smart about refining types within conditional blocks, but not here. Why? And how can I make it work (without just hacking it by relaxing the type of lookup
)?
Answer
You can wrap the name in lookup
check in type guard to help typescript to understand that once condition is true - name
is valid lookup
's key:
>>const getValueOrFalse = (name: string) => {
if (isObjKey(name, lookup)) {
// name narrowed to "foo" | "bar"
return lookup[name]
}
return false
}
function isObjKey<T>(key: PropertyKey, obj: T): key is keyof T {
return key in obj;
}