泛型: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 项目需要并且无法更改)。
所以:
为什么 tsc
在这些情况下选择一个而不是另一个?
如何断言我希望它使用哪一个,或者至少让我的代码在保持 field: keyof T
参数的情况下进行编译?
我现在无法在 2.7.2 上进行测试,但主要问题是 TypeScript 不相信 a[field]
一定是 string
。这是有道理的,因为 a
是通用类型 T
,而 field
是 keyof 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,但如果您运行遇到问题,请告诉我。
希望对您有所帮助。祝你好运!
我经常需要按属性对对象进行排序,所以我编写了以下简单的帮助程序:
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 项目需要并且无法更改)。
所以:
为什么
tsc
在这些情况下选择一个而不是另一个?如何断言我希望它使用哪一个,或者至少让我的代码在保持
field: keyof T
参数的情况下进行编译?
我现在无法在 2.7.2 上进行测试,但主要问题是 TypeScript 不相信 a[field]
一定是 string
。这是有道理的,因为 a
是通用类型 T
,而 field
是 keyof 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,但如果您运行遇到问题,请告诉我。
希望对您有所帮助。祝你好运!