Home > Blockchain >  Generic to check if parameter types of object are equal
Generic to check if parameter types of object are equal

Time:08-10

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.

Playground

  • Related