so I'm trying to make this work: Array<T>.groupBy<KeyType> (property): {key: KeyType, array: Array<T> }[];
The code look like this :
type ArrayByParameter<T, KeyType = any> = string | ((item: T) => KeyType);
declare global {
  interface Array<T> {
    groupBy<KeyType = string>(
      property: ArrayByParameter<T,KeyType>
    ): { key: KeyType; array: T[] }[];
  }
}
if (!Array.prototype.groupBy) {
  Array.prototype.groupBy = function <KeyType = string>(
    property: ArrayByParameter<any, KeyType>
  ) {
    let callbackFunction: (item: any) => any;
    if (typeof property === "string") {
      callbackFunction = (mapObj) => mapObj[property];
    } else if (typeof property === "function") {
      callbackFunction = property;
    } else {
      throw "Parameter is not a string nor a function!";
    }
    // Edit version of : https://stackoverflow.com/a/34890276/3781156
    return Object.entries(
      this.reduce(function (rv, x) {
        (rv[callbackFunction(x)] = rv[callbackFunction(x)] || []).push(
          x
        );
        return rv;
      }, {}) as { [key: string]: Array<any> }
    ).map(([key, array]) => {
      return { key, array };
    });
  };
}
type Characters = "A" | "B" | "C";
type Numbers = "1" | "2" | "3";
type SomeKeyType = `${Characters}-${Numbers}`;
// Same thing as below.
type SomeKeyType2 =
  | "A-1"
  | "A-2"
  | "A-3"
  | "B-1"
  | "B-2"
  | "B-3"
  | "C-1"
  | "C-2"
  | "C-3";
type SomeObject = {
  c: Characters;
  n: Numbers;
  // ...
};
const array: SomeObject[] = [
  { c: "A", n: 1 },
  { c: "A", n: 2 },
  { c: "A", n: 2 },
  { c: "B", n: 1 },
  // ...
];
const groupByArray: { key: SomeKeyType; array: SomeObject[] }[] =
  array.groupBy<KeyType>((obj: SomeObject) => `${obj.c}-${obj.n}`);
Result expected :
[
  {key: "A-1", array: [{c:A, n:1, /*...*/}]},
  {key:"A-2", array: [{/*...*/},{/*...*/}]},
  {key:"B-1", array:[{/*...*/}]},
  /*...*/
];
I'm getting error like this at line 12 at "Array.prototype.groupBy" :
Type '<KeyType = string>(property: ArrayByParameter<any, KeyType>) => { key: string; array: any[]; }[]' is not assignable to type '<KeyType = string>(property: ArrayByParameter<any, KeyType>) => { key: KeyType; array: any[]; }[]'. Type '{ key: string; array: any[]; }[]' is not assignable to type '{ key: KeyType; array: any[]; }[]'. Type '{ key: string; array: any[]; }' is not assignable to type '{ key: KeyType; array: any[]; }'. Types of property 'key' are incompatible. Type 'string' is not assignable to type 'KeyType'. 'KeyType' could be instantiated with an arbitrary type which could be unrelated to 'string'.ts(2322)
I think my KeyType definition is the issue, but I am not able to find a solution. KeyType is a string, but could also be a template literal types which is what I want right now.
So :
- How could I fix the issue so the code work ?
- Is there a way to get T generics in Array.prototype.groupBy function ?
- Right now, T is replace with any, because I don't know how to use T in the prototype definition.
 
Thanks in advance ! :)
 
    