I'm trying to access keys from an object via a function, and if the value doesn't exist, check the next object. I understand the error, but I'm not sure how to handle this situation properly, for example allowing TS to access a missing property.
// constants with transformed values
const firstList = { foo: "a value" }
const secondList = { bar: "another value" }
function getValue( key: keyof typeof firstList | keyof typeof secondList ) {
let val = firstList[key]
if (val != undefined) return val
return secondList[key]
}
Element implicitly has an 'any' type because expression of type '"foo" | "bar"' can't be used to index type '{ foo: string; }'. Property 'bar' does not exist on type '{ foo: string; }'.
CodePudding user response:
Consider changing your implementation, and adding a type guard:
// check if key is in the value and narrows the type of `key`
function isKeyIn<K extends keyof T, T>(key: K, value: T): key is keyof T & K {
return key in value;
}
function getValue( key: keyof typeof firstList | keyof typeof secondList ) {
// if statement with early return would also work
return isKeyIn(key, firstList) ? firstList[key] : secondList[key];
}
See type predicates in the handbook.
CodePudding user response:
Consider this:
// constants with transformed values
const firstList = { foo: "a value" }
const secondList = { bar: "another value" }
const isAllowedKey = <Obj extends Record<PropertyKey, unknown>>(obj: Obj, prop: PropertyKey)
: prop is keyof Obj =>
Object.prototype.hasOwnProperty.call(obj, prop);
function getValue(key: keyof typeof firstList | keyof typeof secondList) {
if (isAllowedKey(firstList, key)) {
let val = key
if (val != undefined) {
return val
}
} else {
return secondList[key]
}
// We should cover all pathes
return undefined
}
As you might have noticed, I have added third path: return undefined
, this is because in order to infer bar
key, you need to put it into else
branch. However with if\else
we ended up in a situation when return undefined
is not reachable but TS complains about covering all pathes