Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Typescript types Object.groupBy as Partial

Tags:

typescript

When I use Object.groupBy in typescript the return type looks something like this Partial<Record<string, T[]>>. But, shouldn't it be just Record<string, T[]>?

The reasoning is: if the groups were created by entries in the original array, why would an entry be missing in the output?

Here is an example:

const people = [
    {name: 'Santi', country: 'Mexico'},
    {name: 'Jacqueline', country: 'France'},
    {name: 'Pablo', country: 'Mexico'},
];
const byCountry = Object.groupBy(people, person => person.country)

// equivalent to
const byCountry: Partial<Record<string, Person[]>> = {
  Mexico: [
    { name: 'Santi', country: 'Mexico' },
    { name: 'Pablo', country: 'Mexico' }
  ],
  France: [
    { name: 'Jacqueline', country: 'France' }
  ],
}

The problem is that since byCountry is partial, I get errors when working with the result

Object.values(byCountry).map(people => people.length) // 'people' is possibly undefined

Whereas with this snippet it all just simply work:

const byCountry2: Record<string, Person[]> = {
  Mexico: [
    { name: 'Santi', country: 'Mexico' },
    { name: 'Pablo', country: 'Mexico' }
  ],
  France: [
    { name: 'Jacqueline', country: 'France' }
  ],
}

Object.values(byCountry2).map(people => people.length)
like image 883
santiago arizti Avatar asked Jun 30 '26 03:06

santiago arizti


1 Answers

In the TypeScript repository issue related to the typings, multiple comments point out that Record would be unsound when the array is empty, which would produce an object with no keys, and thus the typings were updated to use Partial<Record>. (1, 2)

And since Daniel is already comparing this to Object.fromEntries above, I think Ryan’s reasoning on the type correctness there would probably also apply:

It's not correct to use the input type to determine the output type, because knowing what might be in an array is not the same as knowing what's actually in it. This program is legal per the above definitions but unsound [...] (3)

declare function 
    ObjectGroupBy<Item, K extends PropertyKey>(
      items: Iterable<Item>,
      keySelector: (item: Item, index: number) => K,
    ): Record<K, Item[]>;

let x = ObjectGroupBy([] as number[], x => x < NaN ? 'small' : 'large');
//  ^ Record<"small" | "large", number[]>
console.log(x.small) // undefined


  1. https://github.com/microsoft/TypeScript/issues/47171#issuecomment-1848451195
  2. https://github.com/microsoft/TypeScript/issues/47171#issuecomment-1848451195
  3. https://github.com/microsoft/TypeScript/issues/47171#issuecomment-1858674657
like image 96
cbr Avatar answered Jul 04 '26 05:07

cbr