Correcting your implementation
The most straightforward way to fix your implementation is to restrict K
by using extends string
:
type Prop = <K extends string, O extends {}>(k: K, o: O) => /*...*/
The issue arises when you invoke the function as prop("foo", {foo: 'value'})
, causing the type parameters to be inferred as
prop<string, {foo: string}>
. Due to the generic nature of
K
being 'string', it fails the
keyof O
check and yields
"Nah"
.
By applying the extends string
constraint, the type parameters will now be resolved as prop<"foo", {foo: string}>
, delivering the expected functionality.
Nonetheless, this might not align with your intentions. If TypeScript cannot guarantee at compile-time that your key exists in the object, the type will default to "Nah"
:
// TS indicates it always returns "Nah", though the outcome may vary.
function test(obj: object, someKey: string) {
return prop(someKey, obj);
}
Consideration:
An alternative approach is as follows:
function prop1<O>(k: string, o: O): O[keyof O] | "Nah" {
return o.hasOwnProperty(k) ?
o[k as keyof O] : 'Nah';
}
The key distinction from your initial version is the absence of conditional logic: given the uncertainty of whether k
exists as a key within O
, we opt for a simple binary result - either fetching a value from O
(O[keyof O]
) or returning "Nah".
If you have prior knowledge, during compilation, that k
is indeed a key in
O</code, consider implementing overloads in this function to accommodate such scenarios. Nevertheless, managing this could become intricate, suggesting a direct usage of <code>o[k]
or avoiding this function altogether would probably be more effective.