Shane Hsu
Shane Hsu

Reputation: 8357

TypeScript `groupBy` typings

I want to write a groupBy function, so far the type system is not so happy with my work.

export function group<
    T extends object,
    K extends (keyof T & string),
    R = T[K] extends string ? string : never
>(
    data: T[],
    groupBy: keyof T
): { [group: R]: T[] }

The first error that I got is that R in { [group: R]: T[] } is not string or number.

The signature actually doesn't work at all for the data set of

group([{ name: 'Johnny Appleseed', age: 17 }], 'name') // R should be string
group([{ name: 'Johnny Appleseed', age: 17 }], 'age') // R should be never

However, both R is never while K is "name" | "age"

I have to manually narrow down type parameter with group<{name: string, age: number}, 'name'> to make R be string.

Upvotes: 2

Views: 837

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249656

An index signature must be either string or number. No union types, no generic types nothing else will do except string or number.

We can still achieve the behavior you want if you use two function signatures, one that accepts only string keys and the other that only accepts number keys:

export declare function group<T extends Record<K, string>, K extends string>(data: T[], groupBy: K): { [group: number]: T[] }
export declare function group<T extends Record<K, number>, K extends string>(data: T[], groupBy: K): { [group: string]: T[] }


let groupByString = group([{ name: 'Johnny Appleseed', age: 17 }], 'name') 
let groupByNumber = group([{ name: 'Johnny Appleseed', age: 17 }], 'age')

Upvotes: 1

Related Questions