Joji
Joji

Reputation: 5625

TypeScript: How do I properly type this `groupBy` function

Let's say we have the following array of objects and we want to group them by property (here color)

Here is how I wrote it in JS

const users = [
  { name: 'Jim', color: 'blue' },
  { name: 'Sam', color: 'blue' },
  { name: 'Eddie', color: 'green' },
]

function groupBy(array, property) {
  return array.reduce((accu, curr) => {
    const value = curr[property]
    if (accu[value]) {
      accu[value].push(curr)
    } else {
      accu[value] = [curr]
    }
    return accu
  }, {})
}
const usersByColor = groupBy(users, 'color') // TODO: implement groupBy

And I am trying to write it in TypeScript

type GroupedArray<T, K extends keyof T> = {
  [P in T[K]]: T[] // 🚨here it errors on `Type 'T[K]' is not assignable to type 'string | number | symbol'.`
}

function groupBy<T, K extends keyof T>(array: T[], prop: K) {
  return array.reduce((accu, curr) => {
    const value = curr[prop]
    if (accu[value]) {
      accu[value].push(curr)
    } else {
      accu[value] = [curr]
    }
    return accu
  }, {} as GroupedArray<T, K>)
}

const users = [
  { name: 'Jim', color: 'blue' },
  { name: 'Sam', color: 'blue' },
  { name: 'Eddie', color: 'green' },
]

const usersByColor = groupBy(users, 'color')

I got an type error for on

type GroupedArray<T, K extends keyof T> = {
  [P in T[K]]: T[]
}

for Type 'T[K]' is not assignable to type 'string | number | symbol'. I wonder what is the right way to type it?

or is there a better way to write such a function in TypeScript

Upvotes: 1

Views: 326

Answers (2)

martindelophy
martindelophy

Reputation: 71

you should give your params a limit type

type GroupedArray<T extends Record<K, PropertyKey>, K extends keyof T> = {
  [P in T[K]]: T[]
}

function groupBy<T extends Record<K, PropertyKey>, K extends keyof T>(array: T[], prop: K) {
  return array.reduce((accu, curr) => {
    const value = curr[prop]
    if (accu[value]) {
      accu[value].push(curr)
    } else {
      accu[value] = [curr]
    }
    return accu
  }, {} as GroupedArray<T, K>)
}

const users = [
  { name: 'Jim', color: 'blue' },
  { name: 'Sam', color: 'blue' },
  { name: 'Eddie', color: 'green' },
]

const usersByColor = groupBy(users, 'color')

Upvotes: 2

Aplet123
Aplet123

Reputation: 35512

Force the values of T to be string | number | symbol:

type GroupedArray<T extends Record<any, string | number | symbol>, K extends keyof T> = {
  [P in T[K]]: T[]
}

Upvotes: 0

Related Questions