DEV Community

Discussion on: Key Headaches in TypeScript

Collapse
 
pallymore profile image
Yurui Zhang • Edited

I think the Record utility type could help you here:

const getEquivalentKeys = <
    T extends Record<string, any>,
    P extends Record<string, any>
>(
  object1: T,
  object2: P,
): string[] => {

Now you can get even stronger static type checking with the help of another utility type Partial :

const getEquivalentKeys = <
    T extends Record<string, any>,
    P extends Partial<T>
>(object1: T, object2: P): string[] => {

since this function aims to get shared keys with the same values, given a type T - we require that P must contain partially the same keys from T - now TS is able to give you errors before you try to compare two totally different objects:

const o1 = {
    foo: 'bar',
    hi: 'there'
};

const o2 = {
    bar: 'baz',
}

const o3 = {
    bar: 'baz',
    foo: 'foo',
}

getEquivalentKeys(o1, o2); // Error! o2 does not contain any of o1's keys
getEquivalentKeys(o2, o3); // Good
getEquivalentKeys(o1, o3);  // Good

We can do even better here with a more specific return type. Instead of saying it can be an array of any strings, we can assure TypeScript that the result only include shared keys betweenT and P => (keyof T & keyof P)[]

The final function:

const getEquivalentKeys = <T extends Record<string, any>, P extends Partial<T>>(
  object1: T,
  object2: P,
): (keyof T & keyof P)[] => {
  const equivalentKeys: string[] = [];
  Object.keys(object1).forEach(key => {
    if (object1[key] === object2[key]) {
      equivalentKeys.push(key);
    }
  });
  return equivalentKeys;
};

Given your example:

const result = getEquivalentKeys(user1, user2);

without running the code, your editor already knows what are the possible values in the results. (hover your cursor over it to see!)

typescriptlang.org/docs/handbook/u...

Collapse
 
leoat12 profile image
Leonardo Teteo • Edited

Thank you very much for this. I like TS a lot, but I'm still learning and there are some things that you only learn after looking up a lot or by reading the entire docs. I already visited TS docs many times, but never saw this Utility Types section. They should put it more up in the list, in bold red letters. hahaha

Collapse
 
pallymore profile image
Yurui Zhang

I think what might have caused the confusion here is the type object - in TS it's not what you think it is.

The object type refers to anything that is not a number, string, boolean, symbol, null, or undefined (the primitive types). It's actually very similar to any - just a tiny little bit narrower. I rarely find it useful and basically treat it the same way as any (which means - avoid at all costs).

I guess it can be used if you just want an object and don't really care what fields it has (or does it have any fields at all - that's why there are no index signatures on it). In this case since we do care about the parameters having strings as keys, object is not a good fit here.