【问题标题】:Generics: TS2345 Argument of type 'T[keyof T]' is not assignable to parameter of type 'Date' where <T extends {}>泛型:TS2345 类型“T[keyof T]”的参数不可分配给类型为“Date”的参数,其中 <T extends {}>
【发布时间】:2019-01-08 07:32:27
【问题描述】:

我经常要按属性对对象进行排序,所以我写了下面这个简单的助手:

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:“T[keyof T]”类型的参数不可分配给“日期”类型的参数

a[field]b[field] 上。

跟踪WebStorm/TypeScript Service对选择的日期的实现,我注意到在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 参数的同时让我的代码编译?

【问题讨论】:

    标签: typescript generics ecmascript-6 typescript2.0 ecmascript-5


    【解决方案1】:

    我现在无法在 2.7.2 上进行测试,但主要问题是 TypeScript 不相信 a[field] 一定是 string。这是有道理的,因为a 是泛型类型T,而fieldkeyof T 类型,所以它只知道a[field]T[keyof T]可能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&lt;K, string&gt;,也称为{[P in K]: string},或“其键位于K,其值为 string"。所以T[K] 扩展了string 并且很高兴。

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

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

    【讨论】:

    • 确实如此!这正是我正在寻找的通用。非常感谢您的额外解释。
    猜你喜欢
    • 1970-01-01
    • 2020-08-22
    • 2017-07-14
    • 2019-11-17
    • 2020-08-19
    • 1970-01-01
    • 1970-01-01
    • 2021-11-01
    • 2018-09-19
    相关资源
    最近更新 更多