美文网首页
如何在 TypeScript 中迭代对象键

如何在 TypeScript 中迭代对象键

作者: 涅槃快乐是金 | 来源:发表于2024-03-11 08:19 被阅读0次

在 TypeScript 中遍历对象键可能会是一场噩梦。以下是我所知的所有解决方案。

简单概念

使用 Object.keys 进行迭代不起作用,因为Object.keys返回的是一个字符串数组,而不是所有键的联合。这是设计上的,不会改变。

function printUser(user: User) {
  Object.keys(user).forEach((key) => {
    // 不起作用!
    console.log(user[key]);
    // 出错信息如下:
    // Expression 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'.
  });
}

在正确的位置转换为keyof typeof可以使其正常工作:

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

问题在于:使用 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];
// 出错信息如下:
// Expression 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...in 循环

如果你尝试使用for...in 循环,你也会发现它会失败。这是因为相同的原因 - key 被推断为字符串,就像 Object.keys一样。

function printUser(user: User) {
  for (const key in user) {
    console.log(user[key]);
    // 出错信息如下:
    // Expression 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) => {
  // 不再有错误!
  console.log(user[key]);
});

我们也可以在索引对象时执行转换。

在这里,key 仍然被类型化为字符串 - 但在我们索引 user 时,我们将其转换为 keyof typeof user

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

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

const user = {
  name: "Daniel",
  age: 26,
};
 
const nonExistentKey = "id" as keyof typeof user;
 
// 没有错误!
const value = user[nonExistentKey];

在这种情况下,as 是一个相当强大的工具 - 如你所见,它让我们对 TypeScript关于某些事物的类型进行欺骗。

解决方案 2: 类型判断

让我们看一些更智能、可能更安全的解决方案。使用类型谓词怎么样?

通过使用 isKey 辅助函数,我们可以在索引对象之前检查键是否确实在对象上。

通过在 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 在这个主题上的博文

解决方案 3: 泛型函数

让我们看一个稍微奇怪一些的解决方案。在泛型函数内部,使用 in 运算符将会缩小到键的类型。

我实际上不确定为什么这个版本可以工作而非泛型版本不行。

function printEachKey<T extends object>(obj: T) {
  for (const key in obj) {
    console.log(obj[key]);
  }
}
 
// 每个键都会被打印出来!
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);

这可能是最容易被误用的解决方案 - 将转换隐藏在函数内部使其更具吸引力,可能会导致人们在不加思考的情况下使用它。

结论

我的首选解决方案?通常情况下,转换完全可以胜任。它简单易懂 - 通常足够安全。

但如果你喜欢类型谓词或泛型解决方案,那就去试试吧。isKey 函数看起来足够有用。

相关文章

  • python生成器迭代器

    在Python中,迭代器是遵循迭代协议的对象。使用iter()从任何序列对象中得到迭代器(如list, tuple...

  • TypeScript基础入门之迭代器和生成器

    转发 # TypeScript基础入门之迭代器和生成器 迭代性 如果对象具有Symbol.iterator属性的实...

  • 迭代器

    一、迭代器可迭代的(可迭代对象):可迭代对象都是可迭代的。如:str、list、dict、tuple、文件对象等等...

  • Python进阶 - 高性能计算之协程

    迭代器 可迭代对象 什么是可迭代对象 可迭代对象就是对象的类中实现了__iter__方法的对象。对于可迭代对象,可...

  • typecript中的MIxins理解

    知识导向 对象的混入 类的混入 对象的混入 类的混入 下面的代码演示了如何在TypeScript里使用混入。 后面...

  • Python可迭代对象、迭代器与生成器

    本节课纲 可迭代对象 迭代器 生成器Python中内置的序列,如list、tuple、str、bytes、dict...

  • 生成器与迭代器

    一:可迭代对象(Iterable) 1、什么叫可迭代对象? 2、python中那些是可迭代对象 二:迭代器(Ite...

  • TypeScript中的Map

    说明 用于保存键值对。任何值都可作为键或值。 参考 TypeScript Map 对象[https://www.r...

  • Python中的可迭代对象和迭代器对象

    Python中的可迭代对象和迭代器对象是两种不同的东西,可迭代对象是Iterable,迭代器对象是Iterator...

  • python迭代器、生成器与装饰器

    可迭代对象与迭代器 可迭代对象 可以直接作用于for循环的对象统称为可迭代对象,代码中可以通过isinstance...

网友评论

      本文标题:如何在 TypeScript 中迭代对象键

      本文链接:https://www.haomeiwen.com/subject/hrspzdtx.html