I use the following code to validate that only existing keys are accessed on a specific object:
function prop<O, K extends keyof O>(key: K, obj: O) {
return obj[key];
}
This works well enough and will work as expected when using the following code:
const obj = { foo: 'bar' };
prop('foo', obj); // ok
prop('bar', obj); // error
However, I would like to restructure my code to work as follows:
const obj = { foo: 'bar' };
prop('foo')(obj); // should be ok
prop('bar')(obj); // should error
My first idea was to just restructure the function as follows:
function prop<O, K extends keyof O>(key: K) {
return function inner(obj: O) {
return obj[key];
}
}
However, this doesn't work and yields the following error:
Argument of type '"foo"' is not assignable to the parameter of type 'never'.
This makes sense to me, since the object is not available when the key is validated.
Hence, I am aiming to flip the validation logic from "check that the key is present in the object" to "check that the object includes the key" as soon as the object is passed into the second function and error on the type validation of the object if it doesn't, with no other constraints at this specific point.
It feels like this should be possible with TypeScript, but I have not found a way to do this with a completely dynamic key and object, with the only condition for the object being that the given key is present.
Since O is not yet set, you can't have it as a parameter on the first function call (no place you can infer it from). You could of course specify it manually but that is not ideal.
Another option is to spcify the O parameter on the second function. The first call will specify K (as beeing a key, any key). The second call will specify O with the constraint that O must have a key K with any type.
function prop<K extends keyof any>(key: K) {
return function inner<O extends Record<K, any>>(obj: O) : O[K]{
return obj[key];
}
}
const obj = { foo: 'bar' };
prop('foo')(obj); // be ok, returns string
prop('bar')(obj); // is error
If you're happy manually specifying the generic, then the following would work:
function prop<O>(key: keyof O) {
return function inner(obj: O) {
return obj[key];
}
}
prop<{ prop: string }>('prop')({ prop: 'test'}) // compiles
prop<{ prop: string }>('prop')({ wrong: 'test'}) // error
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With