How to check if a strongly typed object contains a given key in TypeScript without it complaining?

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;
}

Playground

© 2024 Dagalaxy. All rights reserved.