javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > TypeScript Object.keys不起作用

在TypeScript中迭代对象键Object.keys不起作用的原因和解决方案

作者:泯泷

在TypeScript中迭代对象键object keys可能是一场噩梦,以下是我所知道的所有解决方案,文中有详细的代码示例供大家参考,具有一定的参考价值,需要的朋友可以参考下

原文链接:https://www.totaltypescript.com/iterate-over-object-keys-in-typescript

在TypeScript中迭代对象键object keys可能是一场噩梦。以下是我所知道的所有解决方案。

快速解释 - 省流

使用 Object.keys 迭代不起作用,是因为 Object.keys 返回一个字符串数组,而不是所有键的并集。这是设计出来的,不会改变。

function printUser(user: User) {
  Object.keys(user).forEach((key) => {
    // Doesn't work!
    console.log(user[key]);
    // Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'User'.
    // No index signature with a parameter of type 'string' was found on type 'User'.
  });
}

解决方法:

const user = {
  name: "Daniel",
  age: 26,
};
 
const keys = Object.keys(user);
 
keys.forEach((key) => {
  console.log(user[key as keyof typeof user]);
});
function isKey<T extends object>(
  x: T,
  k: PropertyKey
): k is keyof T {
  return k in x;
}
 
keys.forEach((key) => {
  if (isKey(user, key)) {
    console.log(user[key]);
  }
});

更长的解释

这里有一个问题:使用Object.keys似乎并不像期望的那样工作。这是因为它没有返回你需要的类型。它不是一个包含所有键的类型,而是将其扩展为一个字符串数组。

const user = {
  name: "Daniel",
  age: 26,
};
 
const keys = Object.keys(user); // const keys: string[]

这意味着你不能使用键来访问对象上的值:

const nameKey = keys[0]; // const nameKey: string


user[nameKey];
// Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ name: string; age: number; }'.
// No index signature with a parameter of type 'string' was found on type '{ name: string; age: number; }'.

TypeScript在这里返回字符串数组有一个很好的原因。TypeScript对象类型是开放式的。

在很多情况下,TS不能保证 Object.keys 返回的键实际上在对象上,因此将它们扩展为字符串是唯一合理的解决方案。查看此问题以了解更多详细信息。

在For循环中

如果你尝试做一个For...在循环中,你也会失败的。这是出于同样的原因——key被推断为字符串,就像 Object.keys 一样。

function printUser(user: User) {
    for (const key in user) {
        console.log(user[key]);
        // Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'User'.
        // No index signature with a parameter of type 'string' was found on type 'User'.
    }
}

但在许多情况下,你实际上能够确认对象是什么样的。所以,你是怎么做的?

解决方案1:转换为 keyof typeof

第一个选项是使用 keyof typeof 将键转换为更特定的类型。在下面的示例中,我们将 Object.keys 的结果转换为包含这些键的数组。

const user = {
  name: "Daniel",
  age: 26,
};
 
const keys = Object.keys(user) as Array<keyof typeof user>;
 
keys.forEach((key) => { 
    // (parameter) key: "name" | "age"
    // No more error!
  console.log(user[key]);
});

我们也可以在索引到对象的时候这样做。在这里, key 仍然是一个字符串类型-但在我们索引到user时,我们将其转换为 keyof typeof user 。

const keys = Object.keys(user);
 
keys.forEach((key) => {
  // (parameter) key: string
  console.log(user[key as keyof typeof user]);
});

然而,以任何形式使用 as 通常都是不安全的,这也不例外。

const user = {
  name: "Daniel",
  age: 26,
};
 
const nonExistentKey = "id" as keyof typeof user;
// const nonExistentKey: "name" | "age"
 
// No error!
const value = user[nonExistentKey];

对于这种情况, as 是一个相当强大的工具-正如你所看到的,它让我们对TypeScript撒谎。

解决方案2:类型谓词 Type Predicates

让我们来看看一些更聪明,可能更安全的解决方案。类型谓词怎么样?通过使用 isKey helper,我们可以在索引到对象之前检查键是否确实在对象上。我们通过在 isKey 的返回类型中使用 is 语法来正确推断TypeScript。

function isKey<T extends object>(
  x: T,
  k: PropertyKey
): k is keyof T {
  return k in x;
}
 
keys.forEach((key) => {
  if (isKey(user, key)) {
    console.log(user[key]);
  }
});

这个令人敬畏的解决方案来自Stefan Baumgartner关于这个主题的伟大博客文章https://fettblog.eu/typescript-iterating-over-objects/

解决方案3:泛型函数

让我们来看看一个稍微奇怪的解决方案。在泛型函数中,使用 in 操作符将类型缩小到键。我真的不知道为什么这里可以这样用,而非泛型版本不行。

function printEachKey<T extends object>(obj: T) {
  for (const key in obj) {
    console.log(obj[key]);  // const key: Extract<keyof T, string>
  }
}
 
// Each key gets printed!
printEachKey({
  name: "Daniel",
  age: 26,
});

解决方案4:在函数中包装Object.keys

另一种解决方案是将 Object.keys 包装在一个返回强制转换类型的函数中。

const objectKeys = <T extends object>(obj: T) => {
  return Object.keys(obj) as Array<keyof T>;
};
 
const keys = objectKeys({
  name: "Daniel",
  age: 26,
});
 
console.log(keys); // const keys: ("name" | "age")[]

这可能是最容易被滥用的解决方案——将转换操作隐藏在函数内部的方法会使其更具吸引力,但可能导致人们在使用时不加思考。

结论

我的首选解决方案?通常情况下,类型转换非常完美地完成了工作。它简单易懂,并且通常足够安全。

但是如果你喜欢类型谓词或通用解决方案的外观,请尽管使用。isKey函数看起来很有用,我会在下一个项目中借用它。

以上就是在TypeScript中迭代对象键Object.keys不起作用的原因和解决方案的详细内容,更多关于TypeScript Object.keys不起作用的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文