typescript - 如何使用通用对象和键类型作为字典中的键

标签 typescript typescript-generics

我正在寻找一个函数,可以使用对象上的任何键过滤通用数组并返回唯一的项目。然而,Typescript 将允许我使用 T[Key] 作为数组,但不能作为字典的键。

此代码有效:

const unique = <T, Key extends keyof T>(array: T[], key: Key): T[] => {
  var uniqueArray: T[Key][] = []
  var distinct: T[] = []

  for (var i = 0; i < array.length; i++) {
    if (!uniqueArray.includes(array[i][key])) {
      distinct.push(array[i])
      uniqueArray.push(array[i][key])
    }
  }

  return distinct
}

export default unique

此代码不起作用:

const unique = <T, Key extends keyof T>(array: T[], key: Key): T[] => {
  var uniqueDict: {[T[Key]]: number} = {}
  var distinct: T[] = []

  for (var i = 0; i < array.length; i++) {
    if (!uniqueDict[array[i][key]]) {
      distinct.push(array[i])
      uniqueDict[array[i][key]] = 1
    }
  }

  return distinct
}

export default unique

第二个代码示例给出错误类型文字中的计算属性名称必须引用其类型为文字类型或“唯一符号”type.ts(1170) 的表达式 var uniqueDict ... 行。

如果我将字典写为 {[id: T[Key]]: number} = {} 它也会给出 索引签名参数类型不能是文字类型的错误或通用类型。考虑在 var uniqueDict ... 行上使用映射对象类型代替 .ts(1337)

关于如何使独特方法的字典版本起作用有什么帮助吗?

最佳答案

第一个问题是这对于您想要执行的操作来说不是有效的语法:

const unique = <T, K extends keyof T>(array: T[], key: K): T[] => {  
  var uniqueDict: { [T[K]]: number } = {} // syntax error

语法{ [xxx]: number }表示类型,其中 xxxcomputed property name因此必须对应于类似键的值。您根本不能在那里使用类型,更不用说 generic 了。 indexed access类型T[K] .

正确的书写方式是使用 mapped type :

const unique = <T, K extends keyof T>(array: T[], key: K): T[] => {
  var uniqueDict: { [P in T[K]]: number } = {}; // error!    
  //                      ~~~~ <-- T[K] isn't known to be keylike

这相当于 Record<T[K], number>使用the Record<K, V> utility type .


但是你看还有一个问题;编译器不知道T[K]是一个类似 key 的类型。毕竟,Tarray 的元素类型,和K类型为key ,它必须是 T 的键。所以T[K]是该键的属性...但该属性本身是 string ?可能不是,任意TK .

为了解决这个问题,我们可以 constrain T这样编译器就知道T[K]可分配给string :

const unique = <T extends { [P in K]: string }, K extends keyof T>(array: T[], key: K): T[] => {
  var uniqueDict: { [P in T[K]]: number } = {}; // error!    
  //  ~~~~~~~~~~ <-- {} isn't known to be assignable to this type

通过约束T{[P in K]: string} (相当于 Record<K, string> ),错误位于 [P in T[K]]已经清空了。


但是我们仍然有一个问题:编译器不认为将一个空对象分配给一个变量是有效的,该变量的类型必须具有类型为 T[K] 的每个键的属性。 。编译器说得有道理;当您分配该空对象时,它没有这样的键。

此时有不同的方法可以继续。你可以向编译器撒个小谎,assert空对象具有这些键:

var uniqueDict = {} as { [P in T[K]]: number }; // okay

或者你可以给 uniqueDict一个indexed access type :

var uniqueDict: { [k: string]: number } = {}; // okay

类型{ [k: string]: number }表示具有任意 string 的对象-valued 键,并且在每个存在的键上,值的类型为 number 。这对于编译器的其余代码来说已经足够了。请注意,默认情况下,编译器将允许您从任意 string 读取。键并表现得好像某个属性确实存在于此处:

uniqueDict.randomKey.toFixed(2); // no compiler error, but probably runtime error

只要您小心不要在代码中做出此类假设,就可以了。否则,您可以打开 the --noUncheckedIndexedAccess compiler option为了更安全(但这更烦人,并且影响的不仅仅是这一段代码),或者您可以显式添加 undefined可能的属性类型 uniqueDict :

var uniqueDict: { [k: string]: number | undefined } = {}; // okay

但是此时整个事情都编译完成了,所以我们可以停止了。

Playground link to code

关于typescript - 如何使用通用对象和键类型作为字典中的键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74553368/

相关文章:

javascript - 如何在 NestJS Controller 处理程序的请求中获取 "validated Body"和 "authenticated user"?

typescript - 如何在 angular.js 2.0 typescript 应用程序中使用谷歌登录按钮?

css - 在 chartjs 中更改饼图和折线图中标签的字体系列

TypeScript 泛型 : infer key of key-value return type from function argument

typescript - 映射类型不是数组类型

TypeScript:仅当 T 属于某种类型时才使通用接口(interface) A<T> 的方法可用

typescript - 解决VS 2017中的 'Conflicting definitions for node' TS4090错误

angular - 如何禁用/抑制 TypeScript 库中的错误?

javascript - 在 TypeScript 中输入向后引用

typescript - 在泛型中使用泛型类类型