TypeScript doesn't currently support partial type parameter inference; if a type has two type parameters, you either have to specify both of them or the compiler will infer both of them. (Well, there are type parameter defaults but that doesn't count as inference either).
There are two workarounds I know of for this. The first is currying, where instead of having a single generic function of multiple type parameters, you have a generic function which returns another generic function. On one of them you specify the type parameter, and the other you let the compiler infer it:
const overPropCurried = <T>() => <P extends keyof T>(
p: P,
over: (val: T[P]) => T[P]
) => {
return (obj: T) => ({
[p]: over(obj[p]),
...obj
});
};
const overPropObj = overPropCurried<Obj>();
overPropObj("a", a => a); // no error
overPropObj("a", a => "a"); // no error
overPropObj("b", b => 1); // no error
overPropObj("c", c => 2); // error, not key 'c' in Obj
overPropObj("a", a => 1); // error, type of key `a` is `string`
The other workaround is to use a single function which takes a dummy parameter corresponding to the type you'd like to specify. The actual value you pass as the dummy parameter doesn't matter because the function implementation ignores it... in fact, as long as the compiler thinks the value is of the right type it doesn't even have to be one at runtime:
const overPropDummy = <T, P extends keyof T>(
dummy: T, // ignored
p: P,
over: (val: T[P]) => T[P]
) => {
return (obj: T) => ({
[p]: over(obj[p]),
...obj
});
};
const dummyObj = null! as Obj; // not really an Obj but it doesn't need to be
overPropDummy(dummyObj, "a", a => a); // no error
overPropDummy(dummyObj, "a", a => "a"); // no error
overPropDummy(dummyObj, "b", b => 1); // no error
overPropDummy(dummyObj, "c", c => 2); // error, not key 'c' in Obj
overPropDummy(dummyObj, "a", a => 1); // error, type of key `a` is `string`
Either way works, and neither way is perfect. I tend to use currying myself, especially if I can use the partial result multiple times (as I've done above with overPropObj
). Anyway, hope that helps; good luck!
Link to code