泛型:TS2345类型“ T [keyof T]”的参数不能分配给类型“ Date”的参数,其中<T扩展{}>

msa*_*ord 2 generics ecmascript-5 typescript ecmascript-6 typescript2.0

我经常必须按属性对对象进行排序,因此我编写了以下简单的帮助程序:

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;
  });
Run Code Online (Sandbox Code Playgroud)

我经常必须按对象上类似日期的属性对对象进行排序,但是我收到的日期是以格式进行字符串化的"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;
  });
Run Code Online (Sandbox Code Playgroud)

导致

TS2345:类型“ T [keyof T]”的参数无法分配给类型“ Date”的参数

a[field]和上b[field]

跟踪WebStorm的/ TypeScript Service的Date的实现,我注意到在中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;
}
Run Code Online (Sandbox Code Playgroud)

但是在我的排序比较器中,它从中lib.es2015.core.d.ts选择接口,该接口只有:

interface DateConstructor {
    new (value: Date): Date;
}
Run Code Online (Sandbox Code Playgroud)

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

我正在使用TypeScript 2.7.2(当前的Angular 6.0.9项目要求该属性,并且不能更改)。

所以:

  1. tsc在这种情况下为什么要选择一个呢?

  2. 如何断言我要使用哪个代码,或者至少在维护field: keyof T参数的同时让我的代码编译?

jca*_*alz 6

我目前无法在2.7.2上进行测试,但是主要问题是TypeScript认为a[field]不一定是string。这是有道理的,因为a是通用类型的T,而且field是类型的keyof T,所以它所知道的a[field]T[keyof T]。这可能string,也可能是东西,你不能打电话new Date()的。

如果您确定只能byDatea[field]a 上调用string,则可以将泛型更改为:

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

那应该使错误消失……它现在知道a[field]类型是type T[K],其中Textends Record<K, string>也称为{[P in K]: string},或者是“其键在其中K且值是string”的对象。如此T[K]扩展string,它很高兴。

上述在打字稿2.7.2工作,但让我知道如果你遇到了一个问题。

希望能有所帮助。祝好运!