我正在尝试实现一个多维数组排序函数,如下所示:https://stackoverflow.com/a/55620991/2175753
它并不太复杂,但是函数的类型设置让我头疼。
这是我到目前为止所拥有的:
export function orderBy<T, U extends keyof T>(array: Array<T>, keys: Array<U>, sort: (key: U, a: T[U], b: T[U]) => number) {
const records = keys.map(key => {
const record = {
key,
uniques: Array.from(new Set(array.map(item => item[key])))
}
record.uniques = record.uniques.sort((a, b) => sort(key, a, b))
return record
})
const weightOfObject = (obj: T) => {
let weight = ''
records.map(record => {
let zeropad = `${record.uniques.length}`.length
weight += record.uniques.indexOf(obj[record.key]).toString().padStart(zeropad, '0')
})
return weight
}
return array.sort((a, b) => weightOfObject(a).localeCompare(weightOfObject(b)))
}
在callsite上,它看起来像这样:
问题是,我无法进一步缩小a和b的类型。我希望这能起作用:
我尝试调整函数签名,以显式地将回调表示为泛型,如下所示:
export function orderBy<T, U extends keyof T>(array: Array<T>, keys: Array<U>, sort: <V extends U>(key: V, a: T[V], b: T[V]) => number) { ... }
但是没有用。
这是TypeScript不支持的,还是我做错了什么?
1条答案
按热度按时间hmtdttj41#
UPD:这个答案部分已经过时了,因为TS 4.x的后期版本引入了去结构化变量之间的跟踪关系。然而,函数参数之间的关系可能永远不会被保留(除了重载函数,但它们在OP的情况下没有用),因此建议的解决方案仍然有效。
某个过时的部件
问题是,只要值的类型在同一个变量中,typescript就只跟踪它们之间的连接,比如一个对象的两个属性或一个元组的两个元素。一旦你将这些值分离到不同的变量中,例如通过解构它们或者只是在函数中传递两个不同的参数,就像你的例子一样,所有的连接都丢失了,并且没有办法恢复它们(至少现在,也许有一天会有可能)。考虑一个可能经常出现的情况
但如果你这么做
你不能做任何事情,你必须把这些值保存在同一个对象中,以便typescript保留关于它们的类型如何相互依赖的任何有用信息。
有效溶液
这就是你在你的场景中可以做的:把这些值放到一个对象中,而不破坏它。这里有一个方法。第一次尝试可能看起来像这样:
但实际上它不会工作,因为在您提供的特定示例中,
T[U]
是number | Date
,因此类型之间的依赖性立即丢失。要保留它,您需要“Map”这个联合类型U
,以便这个联合的每个成员都Map到具有拟合类型的对象。这可以通过像这样的helper类型来实现:所以你创建了一个对象,其中键是
U
的成员,你给每个键分配一个对象,就像这样,严格类型化,然后你得到这些对象的并集,最后是这个[U]
。所以函数看起来像这样:差不多就是这样。你可能会遇到一些问题,当添加实现到这个声明,我不打算在这里输入它,无论如何,你可以抛出一些类型Assert,它会这样做。尽管我很确定你可以在2021年不使用类型Assert来输入它。
现在使用函数 * 而不破坏 * 回调中的参数(至少在typescript已经推断出您需要的所有内容之前)
查看沙箱
我还要注意,你不是在对多维数组进行排序,而是在对对象的一维数组进行排序。请不要插入代码的屏幕截图,不要将实际代码复制为文本,也不要添加描述IDE中这些悬停的注解,从图像中复制测试数据有点烦人。