Dynamic method signature based on prop?
I have a function that will update the field of an object like so:
const obj = {a: 1, b:2};
const changeObj = (field, value) => obj[field] = value;
This is obviously simplified, but I'm trying to make this typed properly based on the field property that is passed in. This is as close as I've gotten:
interface ITest {
name: string;
anotherProp: number;
lastProp: IOtherInterface;
}
const obj: ITest = {name: 'test', anotherProp: 1, lastProp: {a: 'b'}};
const changeObj = (field: keyof ITest, val: ITest[keyof ITest]) => obj[field] = val;
Which gets me most of the way there, but technically val
can have a value type of string, number, or IOtherInterface. Is there a way to tell the function that if I'm passing in name
and val
can only be a string in that case?
Solution 1:
In order for this to work you want changeObj
to be a generic function:
const changeObj = <K extends keyof ITest>(
field: K, val: ITest[K]
) => obj[field] = val; // okay
The field
parameter is of type K
, a type parameter that's constrained to keyof ITest
. When you call changeObj()
, the compiler will infer K
based on what's passed into field
. Then the val
parameter will be checked against ITest[K]
, the type of the value you get if you index into ITest
with a key of type K
:
changeObj("name", "hey") // okay
changeObj("name", 123) // error, number not a string
This makes it much less likely that you'll pass the wrong thing into changeObj
. But it doesn't completely prevent it; if field
is of a union type, then the compiler will also accept a val
of the analogous union type, which could cause problems:
changeObj(Math.random() < 0.99 ? "name" : "anotherProp", 123); // okay?
// uh oh, very good chance of a problem
This sort of unsoundness is just part of TypeScript; the trouble situation doesn't crop up frequently enough to be addressed in the language itself.
Playground link to code