Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ensure object includes dynamic key

Tags:

typescript

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.

like image 623
Laura Avatar asked Jan 25 '26 11:01

Laura


2 Answers

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
like image 139
Titian Cernicova-Dragomir Avatar answered Jan 28 '26 05:01

Titian Cernicova-Dragomir


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
like image 45
Richard Avatar answered Jan 28 '26 05:01

Richard



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!