打字稿中带有索引参数的泛型

时间:2021-01-09 13:35:14

标签: typescript typescript-generics

我想要一个泛型,我可以在保留输入的同时按名称索引。能够这样写会很有用:

candidates.sort(compareByClosestTo("recieved", date))

我觉得很接近,但似乎无法完成这个

// generic comparison fn with index
type Dateable<T> = {
  [P in keyof T]: DateTime
};
const compareByClosestTo = <T extends Dateable<T>>(key: keyof T, date: DateTime) => 
  (l: T, r: T) => Math.abs(l[key].diff(date).milliseconds) - Math.abs(r[key].diff(date).milliseconds)

Playground link

然而,这会产生错误,因为它要求所有参数都为 DateTime 类型。有什么办法可以简单地限制T[key]的类型来输入?

编辑

发布此内容 5 分钟后,我遇到了 a hint here,这导致我:

function sortByClosest<K extends string>(data: Record<K, DateTime>[], key: K, date: DateTime): void {
  data.sort((l, r) => 
    Math.abs(l[key].diff(date).milliseconds) - Math.abs(r[key].diff(date).milliseconds))
}

但是,我想保持开放,因为我真的很想知道如何在回调工厂中找出类型化的通用索引(即,是否可以使用顶级形式?)

1 个答案:

答案 0 :(得分:1)

我认为您的原始 compareByClosestTo() 函数中的主要问题是 T 没有好的推理站点key 参数的类型为 keyof T。如果您传入 "foo"key,则需要编译器推断类型 T,其中 "foo" 是其键之一。编译器可能推断 {foo: DateTime},这取决于您如何使用它,或者它可能会放弃并推断出类似 {foo: any} 甚至 {[k: string]: any} 的东西。您可以期望编译器从 "foo" 类型的值可靠地推断出的唯一内容是字符串 "foo" 本身。

为此,让我们不用 T extends Dateable<T>,而是看看 K extends PropertyKey。然后我们可以用 T 来表达 K:在这种情况下,{ [P in K]: DateTime } 等价于 Record<K, DateTime>(使用 the Record utility type):

const compareByClosestTo = <K extends PropertyKey>(key: K, date: DateTime) =>
  (l: { [P in K]: DateTime }, r: { [P in K]: DateTime }) =>
    Math.abs(l[key].diff(date).milliseconds) - Math.abs(r[key].diff(date).milliseconds);

让我们看看调用它时会发生什么:

const compareFn = compareByClosestTo("recieved", DateTime.local());
/* const compareFn: (l: {
    recieved: DateTime;
}, r: {
    recieved: DateTime;
}) => number */

这里,K 被推断为 "received",结果比较函数就是我们想要的 {received: DateTime} 类型。因此以下两个 sort() 调用都有效:

const works = dates.map(dt => ({ recieved: dt }))
  .sort(compareByClosestTo("recieved", DateTime.local()))

const stillWorks = dates.map((dt, i) => ({ recieved: dt, index: i }))
  .sort(compareByClosestTo("recieved", DateTime.local()))

因为您排序的每个数组都有可分配给 {received: DateTime} 的元素。

Playground link to code