泛型:TS2345 'T[keyof T]' 类型的参数不可分配给 'Date' 类型的参数,其中 <T extends {}>

Generics: TS2345 Argument of type 'T[keyof T]' is not assignable to parameter of type 'Date' where <T extends {}>

我经常需要按属性对对象进行排序,所以我编写了以下简单的帮助程序:

static sort = <T extends {}>(list: Array<T>, field: keyof T, descending: boolean = true) =>
  list.sort((a, b) => {
    if (a[field] > b[field]) {
      return descending ? -1 : 1;
    }
    if (a[field] < b[field]) {
      return descending ? -1 : 1;
    }
    return 0;
  });

我经常需要按对象上类似日期的 属性 对对象进行排序,但我收到的日期以 "01-Apr-2018" 格式进行了字符串化,因此我必须将它们转换为日期首先.

因此,我将上面的内容扩展为:

static byDate = <T extends {}>(list: Array<T>, field: keyof T, descending: boolean = true) =>
  list.sort((a, b) => {
    if (new Date(a[field]).getTime() > new Date(b[field]).getTime()) {
      return descending ? -1 : 1;
    }
    if (a[field] < b[field]) {
      return descending ? 1 : -1;
    }
    return 0;
  });

结果是

TS2345: Argument of type 'T[keyof T]' is not assignable to parameter of type 'Date'

a[field]b[field] 上。

跟踪 WebStorm/TypeScript 服务对所选日期的实现,我注意到在 new Date("hello") 中,它从 lib.es5.d.ts 中选择以下接口:

interface DateConstructor {
    new(): Date;
    new(value: number): Date;
    new(value: string): Date; // THIS ONE (which I expect)
    new(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): Date;
    (): string;
    readonly prototype: Date;
    parse(s: string): number;
    UTC(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): number;
    now(): number;
}

但在我的排序比较器中,它选择了 the interface from lib.es2015.core.d.ts,它只有:

interface DateConstructor {
    new (value: Date): Date;
}

尝试使用 new Date(a[field] as string)as Date 断言无效。

我正在使用 TypeScript 2.7.2(我当前的 Angular 6.0.9 项目需要并且无法更改)。

所以:

  1. 为什么 tsc 在这些情况下选择一个而不是另一个?

  2. 如何断言我希望它使用哪一个,或者至少让我的代码在保持 field: keyof T 参数的情况下进行编译?

我现在无法在 2.7.2 上进行测试,但主要问题是 TypeScript 不相信 a[field] 一定是 string。这是有道理的,因为 a 是通用类型 T,而 fieldkeyof T 类型,所以它只知道 a[field] 是 [=18] =]. 可能string,或者它可能是你不能调用 new Date() 的东西。

如果您确定只会在 a[field]string 的对象上调用 byDate,那么您可以将泛型更改为如下所示:

static byDate = <K extends keyof any, T extends Record<K, string>>(
  list: Array<T>, 
  field: K, 
  descending: boolean = true
) => { ... }

这应该会使错误消失...它现在知道 a[field] 是类型 T[K],其中 T 扩展 Record<K, string>,也称为 {[P in K]: string},或 "an object whose keys are in K and whose values are string"。所以 T[K] 扩展 string 很高兴。

以上应该适用于 TypeScript 2.7.2,但如果您运行遇到问题,请告诉我。

希望对您有所帮助。祝你好运!