Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type 'T[K]' does not satisfy the constraint 'string | number | symbol'

Tags:

typescript

I am creating a simple function to remove duplicates from an array in typescript.

I know there are many different ways to achieve the goal, but my end goal is to learn how types work, so I don't need a solution that differs from my existing code.

My Pseudo code:

  • function will take in array of objects and key of object as parameters
  • array of objects will be converted to a dictionary to only have unique entries
  • construct the array from object and return it.

Working code example using javascript:

function removeDuplicates(arr, propName) {
  const newArr = [];
  const lookup = {};

  for (let i in arr) {
    lookup[arr[i][propName]] = arr[i];
  }

  for (let i in lookup) {
    newArr.push(lookup[i]);
  }

  return newArr;
}

Typescript code (throwing type error)

I am trying to convert that function to Typescript, but stuck at declaring types for lookup variable.

Here is my typescript code:

function removeDuplicates<T, K extends keyof T>(arr: T[], propName: K) {
  const newArr: T[] = [];
  const lookup: Partial<Record<T[K], T>> = {};
                               ^^^^ here is the error

  for (let i in arr) {
    lookup[arr[i][propName]] = arr[i];
  }

  for (let i in lookup) {
    newArr.push(lookup[i]);
  }

  return newArr;
}

Error:

Type 'T[K]' does not satisfy the constraint 'string | number | symbol'

I know why I am getting the error. I am getting the error because value of object.key can be anything. But in my use case, I want to restrict the developer to pass in only the key whose value is either string | number. But I don't know how to do that in typescript.

like image 626
Vishal Avatar asked Oct 24 '25 01:10

Vishal


1 Answers

As you said, you're getting the error because while K is constrained to be a key from the array element type T, there is nothing saying that the property at that key T[K] is itself key-like:

function removeDuplicates<T, K extends keyof T>(arr: T[], propName: K) {
    const lookup: Partial<Record<T[K], T>> = {}; // error!
}

removeDuplicatesOops([{ a: new Date(), b: "hey" }], "a"); // no error

In TypeScript, a type is "keylike" if it's assignable to string | number | symbol, which is given a convenient alias PropertyKey.

We can't constrain T[K] directly, but we can constrain K and/or T so that T[K] is effectively constrained. The easiest way to get your desired behavior where the compiler realizes that Partial<Record<T[K], T>> is an acceptable thing is to constrain T so that the property type at the keys in K must be assignable to PropertyKey:

function removeDuplicates<T extends Record<K, PropertyKey>, K extends keyof T>(
  arr: T[], propName: K) {
    const lookup: Partial<Record<T[K], T>> = {};
    // okay
}

removeDuplicates([{ a: new Date(), b: "hey" }], "a");
// ---------------> ~
// Type 'Date' is not assignable to type 'PropertyKey'.

Yes, the constraints on T and K are circular, but in an allowable way (although it's sometimes tricky to avoid circularity warnings in situations like this). Note that the error appears on the keys of the arr argument and not the propName argument itself. If you really need the latter then you can constrain K even further like

function removeDuplicates<
    T extends Record<K, PropertyKey>,
    K extends keyof { [P in keyof T as T[P] extends PropertyKey ? P : never]: any }
>(arr: T[], propName: K) {
    const lookup: Partial<Record<T[K], T>> = {};
    // okay
}

removeDuplicates([{ a: new Date(), b: "hey" }], "a"); // error!
// -------------------------------------------> ~~~
//Argument of type '"a"' is not assignable to parameter of type '"b"'.

But this might be more complicated than you are looking for (and it would take me too far afield to explain how that K constraint works).

Playground link to code

like image 123
jcalz Avatar answered Oct 26 '25 15:10

jcalz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!