我正在创建一个简单的函数来从打字稿中的数组中删除重复项。
我知道有许多不同的方法可以实现目标,但我的最终目标是了解类型的工作原理,所以我不需要与现有代码不同的解决方案。
我的伪代码:
- 函数将接受对象数组和对象键作为参数
- 对象数组将转换为字典,以便仅具有唯一条目
- 从对象构造数组并返回它。
使用 javascript 的工作代码示例:
function removeDuplicates(arr, propName) {
const newArr = [];
const lookup = {};
for (let i in arr) {
lookup[arr[i][propName]] = arr[i];
}
for (let i in lookup) {
newArr.push(lookup[i]);
}
return newArr;
}
打字稿代码(抛出类型错误)
我正在尝试将该函数转换为 Typescript,但卡在声明查找变量的类型上。
这是我的打字稿代码:
function removeDuplicates<T, K extends keyof T>(arr: T[], propName: K) {
const newArr: T[] = [];
const lookup: Partial<Record<T[K], T>> = {};
^^^^ here is the error
for (let i in arr) {
lookup[arr[i][propName]] = arr[i];
}
for (let i in lookup) {
newArr.push(lookup[i]);
}
return newArr;
}
错误:
Type 'T[K]' does not satisfy the constraint 'string | number | symbol'
我知道为什么我会收到错误。我收到错误,因为对象键的值可以是任何东西。但是在我的用例中,我想限制开发人员仅传入值为字符串的键 |数。但我不知道如何在打字稿中做到这一点。
正如您所说,您会收到错误,因为虽然K
被限制为数组元素类型T
中的键,但没有任何内容表明该键T[K]
的属性本身类似于键:
function removeDuplicates<T, K extends keyof T>(arr: T[], propName: K) {
const lookup: Partial<Record<T[K], T>> = {}; // error!
}
removeDuplicatesOops([{ a: new Date(), b: "hey" }], "a"); // no error
在 TypeScript 中,如果类型可分配给string | number | symbol
,则类型是"类键",它被赋予一个方便的别名PropertyKey
。
我们不能直接约束T[K]
,但我们可以约束K
和/或T
,以便有效地约束T[K]
。在编译器意识到Partial<Record<T[K], T>>
是可以接受的情况下,获得所需行为的最简单方法是约束T
以便K
中键的属性类型必须可分配给PropertyKey
:
function removeDuplicates<T extends Record<K, PropertyKey>, K extends keyof T>(
arr: T[], propName: K) {
const lookup: Partial<Record<T[K], T>> = {};
// okay
}
removeDuplicates([{ a: new Date(), b: "hey" }], "a");
// ---------------> ~
// Type 'Date' is not assignable to type 'PropertyKey'.
是的,对T
和K
的限制是循环的,但以允许的方式(尽管在这种情况下避免循环警告有时很棘手)。 请注意,错误出现在arr
参数的键上,而不是propName
参数本身。 如果你真的需要后者,那么你可以进一步限制K
,比如
function removeDuplicates<
T extends Record<K, PropertyKey>,
K extends keyof { [P in keyof T as T[P] extends PropertyKey ? P : never]: any }
>(arr: T[], propName: K) {
const lookup: Partial<Record<T[K], T>> = {};
// okay
}
removeDuplicates([{ a: new Date(), b: "hey" }], "a"); // error!
// -------------------------------------------> ~~~
//Argument of type '"a"' is not assignable to parameter of type '"b"'.
但这可能比您正在寻找的更复杂(而且我需要太远才能解释该K
约束的工作原理)。
操场链接到代码