I'm trying to write an update function that takes a target object a and it needs to update the key aKey with the value of another object b, with its own key bKey.
I tried using this code:
const updateValue = <T1, T1Key extends keyof T1, T2, T2Key extends keyof T2>(
a: T1,
aKey: T1Key,
b: T2,
bKey: T2Key
) => {
a[aKey] = b[bKey];
// This gives error
// 'T1' could be instantiated with an arbitrary type which could be unrelated to 'T2'.ts(2322)
};
const a = {
a: 1,
b: 'b',
};
const b = {
c: 2,
d: 'd',
};
This is correct behaviour:
updateValue(a, 'a', b, 'c');
This is correct because a on object a exists, and is of type number, same as the key c in object b, which is also a number.
What should not work:
updateValue(a, 'b', b, 'c');
Here the type of key b in object a is of type string, while key c in object b is of type number, this should not be allowed, with above code it works because it only check if both keys exist.
Is there a way to type check this scenario?
CodePudding user response:
Let's change the constraints a bit. Both T1 and T2 should be Records of some kind where K1 and K2 are a key they respectively have.
We can now say that T1 is a Record<K1, any>, because we don't care what the type of the value is here. But for T2, we constrain it to be a Record<K2, T1[K]>. So for the property K2 it should have the same type as T1 has for property K1
const updateValue = <
T1 extends Record<K1, any>,
T2 extends Record<K2, T1[K1]>,
K1 extends string,
K2 extends string
>(
a: T1,
aKey: K1,
b: T2,
bKey: K2
) => {
a[aKey] = b[bKey] as any;
};
We also have to supress the assignment error by using a type assertion.
