I've been working on this code snippet and I'm trying to figure out how to make it work:
Array<T>.groupBy<KeyType> (property): {key: KeyType, array: Array<T> }[];
The code looks 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 encountered the following error at line 12 in '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 suspect that the issue lies within my KeyType definition, but I'm struggling to find a solution. KeyType is defined as a string but can also be a template literal type, which is what I currently require.
So, my questions are:
- How can I resolve the issue and get the code to work?
- Is there a way to incorporate T generics in the Array.prototype.groupBy function?
- Currently, T is replaced with any because I'm unsure how to utilize T in the prototype definition.
Thank you in advance! :)