groupby-typename
Version:
Type-safe and tested utility to easily group an array of GraphQL objects by their typename
82 lines (55 loc) • 4.05 kB
Markdown
# groupby-typename
<img height="100" src="https://github.com/user-attachments/assets/c56e1a32-fba3-4fdb-bdc7-ea8743f7298a" alt="library's logo" align="right">
[](https://www.npmjs.org/package/groupby-typename)
[](https://www.npmjs.org/package/groupby-typename)
[](https://codecov.io/gh/charpeni/groupby-typename)
[](https://github.com/charpeni/groupby-typename/blob/main/LICENSE)
Type-safe and tested utility to easily group an array of GraphQL objects by their typename
<br />
## Why
GraphQL can return a list of objects of different types (nodes or union of types). This utility allows you to group them by their `__typename` field, but also ensure that they are typed accordingly.
By default, using a naive group by doesn't offer a type-safe way to access the grouped objects. Let's say we have an array that contains `User` and `Post`: `Array<User | Post>`, we would expect the returned type to be something like:
```ts
{
User?: User[];
Post?: Post[];
}
```
Unfortunately, it won't be the case, it will either be `Record<string, (User | Post)[]>` or `Record<"User" | "Post", (User | Post)[]>`.
Leading to confusing usages where accessing a key for a given `__typename` won't narrow down the type:
```ts
const grouped = Object.groupBy(data, (item) => item.__typename);
// ^? Partial<Record<"User" | "Post", (User | Post)[]>>
const users = grouped.User;
// ^? const users: (User | Post)[] | undefined
```
See this example in [TypeScript Playground](https://www.typescriptlang.org/play/?target=11&q=291#code/C4TwDgpgBAqgzhATlAvFA3gKClA+r0SAOwEMBbCALigHJ4kaAabKASwBNqiBXMgIyQBuFqQrU4wRKyIBzZgF9hmQtAAKAewmoMLfCtFVaGiUxYcuvAYmE5grYABtDEqbIVKAxuqJb2JYCTUAIKIiCQgADz0yAA+UMbAAHzaANos6HgE4BAG1HQIiDRQJHBQXj7AjGycUACMVbm0QQ6sHhBF8sw4GXrZjTQJRSVl3hJV5lAATFV2joY0ABIQDg7qUADq6ogO7B1dGJn65PPRQ6XlY9XUAMwNx3kAQup8e+mHffdGmsBnIxXjNQALDN7E48gAVbIAZQ8UjAwCg4NYYDgrwAukoAPSYqAPACiADEAPIAJTxmEwFwRMkQ6m4kHYDwgADMttA0ES+AArCAeYAAOhpdLADxAAAo-AEqmL7BAyABKVDJWVkfm9YjHeXCbE4HAAPQA-BSqVBuAU4EzWYh2VAhfSIIyWWz+dFtTjdYaKTqggTwXiSRTWGQwFsERk7SKQJCNRQoPIoMzaWRaBG+CAALRHCg0Tyjam0+3sILM4BIbQR0XRnLHCX+EhazA6j0Gv5aCMO6gZaIG6jRFIY+LfHuDiT9wRx41503m4ul5BodtFktIF0FN26qCG1sIs1IOC9gr9qBxbhEdgs6QOzBAA).
This utility aims to solve this issue by providing a type-safe way to access the grouped objects.
## Installation
```sh
npm install groupby-typename
```
## Usage
> [!NOTE]
> This utility is built with [`Object.groupBy`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/groupBy)—which is part of the new baseline for 2024. Currently supported by all major browsers, but requires TypeScript 5.4 and above (you will need to target at least `ES2024`), or Node 21 and above.
>
> If you do not meet those requirements, don't worry, the utility will fallback to a legacy implementation based on a reducer.
```ts
import { groupByTypename } from 'groupby-typename';
type User = { __typename: 'User'; id: number; name: string };
type Post = { __typename: 'Post'; id: number; title: string };
const data: Array<User | Post> = [
{ __typename: 'User', id: 1, name: 'Alice' },
{ __typename: 'User', id: 2, name: 'Bob' },
{ __typename: 'Post', id: 1, title: 'Hello, world!' },
{ __typename: 'Post', id: 2, title: 'GraphQL is awesome!' },
];
const grouped = groupByTypename(data);
// ^? const grouped: { User?: User[]; Post?: Post[]; }
const users = grouped.User;
// ^? const users: User[] | undefined
```
> [!NOTE]
> Because they are not runtime types, the union of types is necessarily a superset of the types in the array at runtime. Therefore, the returned keys are always optional, as we can't know if at least one object of a given type is present in the array.
## License
groupby-typename is [MIT licensed](LICENSE).