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.