#Ensuring two object arguments have the same key

26 messages · Page 1 of 1 (latest)

quiet aurora
#

I run into this sort of issue fairly frequently, especially when trying to convert generic functions to ts.

In the example below I need to compare objects with different structures but both of which have the same key. For example they both represent the same DB object with the same id key but are types of different fidelity.

// Basic pseudocode of what I want
export const compare = <
  Item,
  NewItem,
  SharedKey extends keyof Item & keyof NewItem,
>(
  oldThing: Item,
  newThing: NewItem,
  idKey: SharedKey,
) => {
  return oldThing[idKey] === newThing[idKey];
}

const example1 = {id: 1, name: 'bob'}
const example2 = {id: 1, name: 'bob': age: 20}
compare(example1, example2, 'id') // true

const broken1 = {id: 'axy', name: 'bob'}
compare(broken1, example2, 'id') // this should give type errors as both objects don't have a shared 'id' key with the same type
modern plover
#

One simple solution:

const compare = <K extends PropertyKey, V>(
    objA: Record<K, V>,
    objB: Record<K, V>,
    key: K,
) => objA[key] === objB[key]

This however will not prevent the two objects having different types on the property. If you want that you can do:

const compare = <K extends PropertyKey, V>(
    objA: Record<K, V>,
-   objB: Record<K, V>,
+   objB: Record<K, NoInfer<V>>,
    key: K,
) => objA[key] === objB[key]
#

But honestly, I don't see the point of doing this than just doing example.id === broken.id directly. TS will also warn you about them having different types:

marble sequoiaBOT
#
const example = { id: 1 }
const broken = { id: 'axy' }
example.id === broken.id
//^^^^^^^^^^^^^^^^^^^^^^
// This comparison appears to be unintentional because the types 'number' and 'string' have no overlap.
modern plover
#

Kind of feels like you are reinventing ===.

quiet aurora
#

this is a simple example to help illustrate the class of problem where i need to ensure objects are "related" i suppose

#

often its more like grouping or classifying with id being the common link etc. and usually i'm operating on arrays of said types rather than single objects

#

I suppose a more complete example of something i'm trying to do at the moment is something like this that is grouping items from a collection into new/updated/deleted by comparing an old collection and a new collection:

export const groupCollectionByCrudType = <
  SharedKey extends keyof any,
  ValueType extends string | number,
  Item extends WithSharedKey<SharedKey, ValueType>,
  NewItem extends WithSharedKey<SharedKey, ValueType>,
>(
  initialCollection: Item[],
  newCollection: NewItem[],
  idKey: SharedKey,
) => {
  const createCollection: NewItem[] = []
  const updateCollection: NewItem[] = []
  const deleteCollection: Item[] = []

  // implementation

  return {
    createCollection,
    updateCollection,
    deleteCollection,
  }
}
#

that WithSharedKey thing doesn't work, It was just the latest attempt i made to try ensure they were compatible. Ideally I want to preserve those types as i return the collections

modern plover
#

Have you tried the solution I've given?

#

If that doesn't work for your complete code, you should make a TS playground reproduction.

quiet aurora
#

still typing, give me a sec

#

how do I share it? the bot just deleted it

modern plover
#

That's already shared.

quiet aurora
#

hm?

modern plover
#

Uh, that bot message contained your code

#

But you've just deleted it.

quiet aurora
#

confusing, sorry i just clicked the bin to see what it was

marble sequoiaBOT
#
ticketmanbob#0

Preview:```ts
export const groupCollectionByCrudType = <
K extends PropertyKey,
V

(
initialCollection: Array<Record<K, V>>,
newCollection: Array<Record<K, NoInfer<V>>>,
idKey: K
) => {
const createCollection: typeof newCollection[] = []
const updateCollection: typeof newCollection[] =
...```

modern plover
#

When you post a TS playground link, the bot turns it into an embed preview so that the link isn't taking up two screens.

quiet aurora
#

and so it just doesn't show for me?

#

i see a bin icon and nothing else

modern plover
#

It shows fine for me, sounds like you might need to update your Discord.

#

Well, here's a solution:

marble sequoiaBOT
#
nonspicyburrito#0

Preview:```ts
export const groupCollectionByCrudType = <
K extends PropertyKey,
TOld extends Record<K, unknown>,
TNew extends Record<K, TOld[K]>

(
initialCollection: TOld[],
...```

quiet aurora
#

awesome thankyou