将 Type 约束为 T 的键并且是特定类型

Constrain Type to be a keyof T and to be a specific type

考虑

export function sortByProp<T extends Record<string, any>>(propName: keyof T) {
  return sort((a, b) => a[propName].localeCompare(b[propName]))

我希望确保 propName 也是一个类型 string

结果

Property 'localeCompare' does not exist on type 'unknown'.ts(2339)

这是如何实现的?

好的,这让我更进一步:

export const sortByProp = <T extends Record<string, any>>(propName: keyof T) => {
  return sort<T>((a, b) => a[propName].localeCompare(b[propName]))
}

消耗

 sortByProp('hostname')(data.devices.devicePageEntries).map(x => ({
          key: x.id,
          value: x.hostname,
        }))

但后来我得到

Property 'id' does not exist on type '{ hostname: any; }

在我的调用方方法中进行转换似乎可以解决问题,

 (sortByProp('hostname')(
          data.devices.devicePageEntries,
        ) as DevicePageEntry[])

我正在使用 Ramda 0.25.0

这是一个可能的解决方案:

import { sort } from "ramda";

export function sortByProp<K extends string>(propName: K) {
  return <T extends Record<K, string>>(arr: T[]) =>
    sort((a: T, b: T) => a[propName].localeCompare(b[propName]))(arr);
}

const result = sortByProp("hostname")([
  { hostname: "foo", id: 1 },
  { hostname: "bar", id: 2 }
]); 
// [{"hostname":"bar","id":2},{"hostname":"foo","id":1}]

const mappedResult = result.map(x => ({
  key: x.id, // works
  value: x.hostname
})); 
// [{"key":2,"value":"bar"},{"key":1,"value":"foo"}]

Code sample on StackBlitz

我用约束 extends Record<K, string> 声明了类型参数 T 以确保 localeCompare 总是在 string 属性 值上被调用。

此外,编译器无法通过查看函数声明签名(参数和 return 类型来收集 generic type parameter T by solely looking at function parameter propName: keyof T, so it takes any from T's base constraint Record<string, any>. Further reason is: Inference candidatesT 的 属性 值类型),而不是函数 body/implementation。 TypeScript 也只直接咨询带有 T 注释的函数,而不是内部函数声明,以供可能的候选者使用。

通过将 T 声明为内部函数的一部分,TypeScript 可以利用函数参数 arr: T[] 来推断 T.

另类 sort 纯 JS

function sort<T>(cb: (t1: T, t2: T) => number) {
  return (arr: T[]) => [...arr].sort(cb);
}

Playground