我正在寻找一个函数,可以使用对象上的任何键过滤通用数组并返回唯一的项目。然而,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 }
表示类型,其中 xxx
是 computed 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 的类型。毕竟,T
是 array
的元素类型,和K
类型为key
,它必须是 T
的键。所以T[K]
是该键的属性...但该属性本身是 string
?可能不是,任意T
和K
.
为了解决这个问题,我们可以 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
但是此时整个事情都编译完成了,所以我们可以停止了。
关于typescript - 如何使用通用对象和键类型作为字典中的键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74553368/