首页 > 解决方案 > 通用接口中的通用原型函数?大批。通过...分组(道具):{键:K,数组:T[]}[]

问题描述

所以我试图使这项工作:Array<T>.groupBy<KeyType> (property): {key: KeyType, array: Array<T> }[]; 代码如下所示:

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:[{/*...*/}]},
  /*...*/
];

我在“Array.prototype.groupBy”的第 12 行遇到这样的错误:

类型 '<KeyType = string>(property: ArrayByParameter<any, KeyType>) => { key: string; 数组:任何[];}[]' 不可分配给类型 '<KeyType = string>(property: ArrayByParameter<any, KeyType>) => { key: KeyType; 数组:任何[];}[]'。键入'{键:字符串;数组:任何[];}[]' 不可分配给类型 '{ key: KeyType; 数组:任何[];}[]'。键入'{键:字符串;数组:任何[];}' 不可分配给类型 '{ key: KeyType; 数组:任何[];}'。属性“键”的类型不兼容。类型“字符串”不可分配给类型“KeyType”。'KeyType' 可以用与 'string'.ts(2322) 无关的任意类型实例化

我认为我的 KeyType 定义是问题所在,但我无法找到解决方案。KeyType 是一个字符串,但也可以是我现在想要的模板文字类型。

所以 :

提前致谢 !:)

标签: typescriptgenericstypescript-generics

解决方案


我该如何解决这个问题以便KeyType匹配?

这里的关键问题Object.entries是当前将key索引器硬编码为string而不是允许K extends PropertyKey(PropertyKey是可以在 JavaScript 中索引到对象中的所有类型的内置类型别名)。

没有什么是我们无法通过额外的环境类型定义来解决的:

declare module "our-global-ambient-module" {
  global {
    interface Array<T> {
      groupBy<KeyType extends PropertyKey = string>(
        property: ArrayByParameter<T, KeyType>
      ): { key: KeyType; array: T[] }[];
    }

    // What we've added - an overload to `Object.entries`
    // that will preserve the key's type.
    interface ObjectConstructor {
      entries<K extends PropertyKey, V>(object: any): [K, V][];
    }
  }
}

操场上的完整代码在这里

有没有办法在函数中获得T泛型Array.prototype.groupBy

是的,只需添加一个类型参数和类型this为该类型:

Array.prototype.groupBy = function <T, KeyType extends PropertyKey = string>(
  this: T[],
  property: ArrayByParameter<any, KeyType>
) 

您还需要{}reduce通话中注释类型:

{} as {[X in KeyType]: T[]}

推荐阅读