TypeScript中遍历对象键的方法
作者:黑土豆
前言
在日常的TypeScript
开发中,经常需要遍历对象的键来执行各种操作。然而,使用Object.keys
时可能会遇到一些类型相关的困扰,因为它返回的是一个字符串数组,而不是期望的键的联合类型。这可能导致在代码中引入一些不安全的类型转换。在本文中,我们将深入研究这个问题,并提供几种解决方案,以便在遍历对象键时更安全、更灵活地操作。
背景
使用Object.keys
进行遍历并不能按照预期工作。这是因为Object.keys
返回一个字符串数组,而不是包含所有键的联合类型。这是设计上的考虑,不会改变。
function printUser(user: User) { Object.keys(user).forEach((key) => { // 不起作用! console.log(user[key]); // 报错:属性“key”在类型“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]); // key现在被缩小为 "name" | "age" } });
Object.keys
问题在于使用Object.keys
似乎无法按照期望的方式工作。这是因为它不会返回你需要的类型。
const user = { name: "Daniel", age: 26, }; const keys = Object.keys(user); // keys的类型是 string[]
这意味着你不能使用键来访问对象上的值:
const nameKey = keys[0]; user[nameKey]; // 报错:属性“nameKey”在类型“{ name: string; age: number; }”上不存在。
TypeScript
之所以返回字符串数组,是因为它的对象类型是开放的。在许多情况下,TS
无法保证由Object.keys
返回的键实际上存在于对象上 - 因此将它们扩展为字符串是唯一合理的解决方案。
For...in循环
如果尝试使用for...in
循环,同样会失败,原因是键被推断为字符串,就像Object.keys
一样。
function printUser(user: User) { for (const key in user) { console.log(user[key]); // 报错:属性“key”在类型“User”上不存在。 } }
但在许多情况下,你可能确信自己完全了解对象的形状。
那么,怎么办呢?
解决方案
解决方案1:转换为keyof typeof
第一种选择是使用keyof typeof
将键转换为更具体的类型。
const user = { name: "Daniel", age: 26, }; const keys = Object.keys(user) as Array<keyof typeof user>; keys.forEach((key) => { // 不再报错! console.log(user[key]); });
在索引对象时也可以进行转换。
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
助手,可以在索引之前检查键是否实际存在于对象中。
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]); // key现在被缩小为 "name" | "age" } });
解决方案3:泛型函数
再来看一个略微奇怪的解决方案。在泛型函数内部,使用in
运算符将类型缩小到键。
function printEachKey<T extends object>(obj: T) { for (const key in obj) { console.log(obj[key]); // key的类型被缩小为Extract<keyof T, string> } } // 每个键都被打印出来! 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); // keys的类型是("name" | "age")[]
这可能是最容易被滥用的解决方案 - 将转换隐藏在函数中使其更有吸引力,可能导致人们在不考虑的情况下使用它。
结论
本文介绍了一些解决方案,从简单的类型转换到更智能的类型谓词,帮助我们更安全、更可靠地进行对象键的遍历。选择哪种方法取决于项目的需求和个人偏好,但总体而言,通过了解这些技术,我们可以更好地利用TypeScript
的类型系统,提高代码的可维护性和安全性。
以上就是TypeScript中遍历对象键的方法的详细内容,更多关于TypeScript遍历对象键的资料请关注脚本之家其它相关文章!