Creating a generic groupBy function using Typescript

Questioning the TypeScript compiler's comprehension of my groupBy function, which includes an optional transformer for post-grouping data modification. Seeking suggestions on what steps to take next.

const customGrouping = <A extends {}, C, B extends keyof any>(
  extractKey: (o: A) => B,
  data: A[],
  transformer?: (o: A) => C
) =>
  data.reduce(
    (acc, cur) => ({
      ...acc,
      [extractKey(cur)]: (acc[extractKey(cur)] ?? []).concat(
        transformer ? transformer(cur) : cur
      ),
    }),
    {} as Record<B, (A | C)[]>
  )

// Implementation

const myList = [{ a: 1 }, { a: 2 }, { a: 3 }]

const result1 = customGrouping(
  (x) => x.a,
  myList,
)

//Actual type of result1: Record<number, unknown[]>
//Desired type of result1:  Record<number, {a: number}[]>

const result2 = customGrouping( 
  (x) => x.a,
  myList,
  (x) => ({ b: x.a })
)

//Actual type of result2: Record<number, ({ a: number; } | { b: number; })[]>
//Desired type of result2:  Record<number, {b: number}[]>

Answer №1

To achieve the desired outcome, you can utilize function overloading.

// Overloading without transformer
export function groupByList<A extends {}, B extends PropertyKey>(
  extractKey: (o: A) => B,
  data: A[],
): Record<B, A[]>;

// Overloading with transformer
export function groupByList<A extends {}, B extends PropertyKey, C>(
  extractKey: (o: A) => B,
  data: A[],
  transformer: (o: A) => C,
): Record<B, C[]>;

// Implementation
export function groupByList<A extends {}, B extends PropertyKey, C>(
  extractKey: (o: A) => B,
  data: A[],
  transformer?: (o: A) => C,
) {
  // Type added for reduce to prevent index issues.
  // Partial added to handle errors with initial value in reduce
  return data.reduce<Partial<Record<B, (A | C)[]>>>(
    (acc, cur) => ({
      ...acc,
      [extractKey(cur)]: (acc[extractKey(cur)] ?? []).concat(transformer?.(cur) ?? cur),
    }),
    {},
  );
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Generating a custom type in Typescript based on specific array keys

Imagine I have a structure like this: export interface MyObject { id: number title: string } and then I create an array as shown below: const myArray: MyObject[] = [ { id: 2358, title: 'Item 1' }, { id: 85373, title: &a ...

Is there a way to enable autofill functionality if an email already exists in the database or API within Angular 12?

In order to auto-fill all required input fields if the email already exists in the database, I am looking for a way to implement this feature using API in Angular. Any guidance or suggestions on how to achieve this would be greatly appreciated. ...

reactjs: disable a specific eslint rule

I'm trying to figure out how to disable the "no-unused-vars" rule specifically for TypeScript interfaces. Here's a code snippet where I'm getting a warning: export type IKeoTableColumn<T> = { id: T; align: 'left' | ' ...

What is the simplest way to incorporate Vue with Typescript, without the need for a complex build setup?

I've been spending the last couple of days experimenting with my basic ASP.NET Core website set up for Typescript 2.9, but unfortunately, I haven't made much progress. My main goal is to keep the front-end simple, with just single Vue apps on eac ...

Searching and adding new elements to a sorted array of objects using binary insertion algorithm

I'm currently working on implementing a method to insert an object into a sorted array using binary search to determine the correct index for the new object. You can view the code on codesanbox The array I have is sorted using the following comparis ...

Recursive type analysis indicates that the instantiation of the type is excessively deep and may lead to potential infinite loops

In the process of developing a Jest utility, I have created a solution where an object mock is lazily generated as properties are accessed or called. Essentially, when a property is invoked like a method, it will utilize a jest.fn() for that specific path ...

How is it possible for TypeScript to enable the importing of dependencies that it ultimately cannot utilize during runtime?

Take a look at my sample project by following this link: https://github.com/DanKaplanSES/typescript-stub-examples/tree/JavaScript-import-invalid I have developed a file named main.ts: import uuid from "uuid"; console.log(uuid.v4()); While type ...

Angular interceptor allows the execution of code after the outgoing request has completed its process

In the process of creating a simple interceptor, I have encountered an issue. The interceptor is designed to check if an outgoing request is targeting a specific endpoint type, namely events and favoriteevents. While the interceptor works almost as intend ...

Using TypeScript's Non-Null Assertion Operators in combination with square brackets []

One way to assert that an object has a certain property is by using the `!.` syntax as shown below: type Person = { name: string; age: number; gender?: string; } const myPerson: Person = { name: 'John Cena', age: 123, gender: 's ...

Using Regular Expressions: Ensuring that a specific character is immediately followed by one or multiple digits

Check out the regular expression I created: ^[0-9\(\)\*\+\/\-\sd]*$ This regex is designed to match patterns such as: '2d6', '(3d6) + 3', and more. However, it also mistakenly matches: '3d&apos ...

What is the approach taken by this component to display its child elements?

While delving into the code of react-accessible-accordion, I found myself puzzled by the way it handles rendering its children. The snippet below is from Accordion.tsx: export default class Accordion extends React.Component<AccordionProps> { // ...

What is the reason for TypeScript not providing warnings for unrealistic conditions involving 'typeof' and 'in'?

The recent updates in version 4.9 highlighted the enhanced narrowing with 'in'. Intrigued by this, I decided to experiment with their example in a coding playground. Surprisingly, I discovered that seemingly impossible conditions involving typeof ...

Exploring table iteration in Angular 7

I am looking to create a table with one property per cell, but I want each row to contain 4 cells before moving on to the next row... This is what I want: <table> <tr> <td> <mat-checkbox>1</mat-checkbox& ...

What is the secret to getting this nested observable to function correctly?

Currently, I am working on an autocomplete feature that dynamically filters a list of meals based on the user's input: export class MealAutocompleteComponent { mealCtrl = new FormControl() filteredMeals: Observable<Array<Meal>> live ...

What is the proper way to declare a Type for a JSX attribute in Google AMP that utilizes square brackets?

When utilizing AMP's binding feature, you must apply specific attributes that encapsulate an element's property with square brackets and connect it to an expression. An example from AMP is shown below: <p [text]="'Hello ' + foo"> ...

The pagination feature in ag-grid is malfunctioning, causing it to initially send a request to

Upon clicking the search button, a server call will be made to retrieve results and display them in the ag grid. The server will only return the specified number of records based on the pagination details provided with each click. Despite implementing the ...

The webpack development server refreshes the page just one time

I've been searching through various issues on Stack Overflow but haven't had any luck. It seems like most solutions are for an older version of webpack-dev-server. Despite trying numerous things, my app just won't reload or rebuild more tha ...

RouterModule is a crucial external component that is essential for integrating

If I have a very simple component that is part of an Angular component library, it might look like this: mycomponent.module.html <div> <a routerLink="/"> </div> mycomponent.component.ts import { Component, OnInit, Input } from &a ...

Angular 14: Trouble with ngFor after latest update - Type 'unknown' causing issues

Just updated my project to angular version 14.0.4 Within the html of a component, I have the following code: <div class="file" *ngFor="let file of localDocumentData.files; index as i;"> <div class="card"> ...

I am facing the dilemma of having an identical button appearing in two separate locations. How can I determine which button has been clicked?

I am currently using ng2-smart-table and have implemented a custom filter with the same button in both filters. However, I am unsure of how to determine which button is being clicked. https://i.stack.imgur.com/b1Uca.png Below is the component code for th ...