类型 'T[K]' 不满足约束'string | number | symbol'



我正在创建一个简单的函数来从打字稿中的数组中删除重复项。

我知道有许多不同的方法可以实现目标,但我的最终目标是了解类型的工作原理,所以我不需要与现有代码不同的解决方案。

我的伪代码:

  • 函数将接受对象数组和对象键作为参数
  • 对象数组将转换为字典,以便仅具有唯一条目
  • 从对象构造数组并返回它。

使用 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'.

是的,对TK的限制是循环的,但以允许的方式(尽管在这种情况下避免循环警告有时很棘手)。 请注意,错误出现在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约束的工作原理)。

操场链接到代码

最新更新