You can do what you want with this typeguard, copied straight from the accepted answer:
function isObjKey<T>(key: any, obj: T): key is keyof T {
return key in obj;
}
However please note that as standalone function this is wrong in typescript given the fact that object types are open. You can get weird typing with this. This for example is valid code:
interface X = { a: number;}
const x = { a: 0, b: 'hello' }
const y: X = { a: 0 };
const z: X = x;
const key: string = 'b';
if (isObjKey(key, z)) {
// here, key is typed as 'a', but key === 'b'
const a = z[key]; // oh no, a string statically typed as number
const b = x[key]; // again, a string statically typed as number
const c = y[key]; // and here we have undefined typed as number
const e = c.toString(); // crash, TypeError: cannot read property 'value' of undefined
}
The point is that key in obj
tests for properties in the dynamic type of the object, not in the static type definition. And types being open in typescript mean that any object adhere to an interface even if it has more properties than needed.
Since key in obj
doesn't imply key is keyof typeof obj
and typescript doesn't let you do these mistakes freely, with if (key in obj) { ... }
. You have to write this broken typeguard manually and assume all responsibility that comes with it. This is the same reason for which Object.keys(x)
returns a string[]
instead of an Array<keyof typeof x>
(or a more complicated mapped type to only include string
keys).
That said, in some cases you may find this kind of broken type guard functions useful, but you need to be certain that the dynamic type of the object you are passing as parameter coincides with its static type. And for this reason I find that the best approach is to write an explicit type assertion, with a comment explaining why the assertion holds in this particular case, which is less likely to pass code review unnoticed:
const getValueOrDefault = (name: string, defaultValue: number) => {
if (name in lookup) {
// lookup never has more keys than what explicitly
// written in its static type because blah blah blah...
// so this assertion is safe
return lookup[name as keyof lookup]
}
return defaultValue
}