TypeScript 进阶:如何精准获取对象的所有 Key?

🔑 TypeScript 进阶:如何精准获取对象的所有 Key?

🤔 为什么这不仅仅是 Object.keys() 的问题?

在 JavaScript 中,Object.keys(obj) 返回一个字符串数组,这没问题。但在 TypeScript 中,如果你直接用它来访问对象属性,编译器会报错:

typescript 复制代码
const user = { name: "Alice", age: 25 };

// ❌ 错误演示
const keys = Object.keys(user); // 类型是 string[]
keys.forEach((key) => {
  console.log(user[key]);
  // ^^^ Error: Element implicitly has an 'any' type because index expression is not of type 'number'.
  // TS 不知道 key 是 "name" 还是 "age",它只认为 key 是任意字符串
});

我们需要的是:既拿到运行时的 Key 值,又保留编译时的类型信息


📂 目录

  1. [🛠️ 运行时获取:Object.keys 的类型断言](#🛠️ 运行时获取:Object.keys 的类型断言)
  2. [🧩 编译时提取:keyof 操作符](#🧩 编译时提取:keyof 操作符)
  3. [🚀 高级玩法:泛型与工具类型结合](#🚀 高级玩法:泛型与工具类型结合)
  4. [⚠️ 常见陷阱:索引签名与可选属性](#⚠️ 常见陷阱:索引签名与可选属性)
  5. [💡 总结](#💡 总结)

1. 🛠️ 运行时获取:Object.keys 的类型断言

如果你需要在代码逻辑中遍历对象,必须解决 Object.keys 返回 string[] 的问题。

✅ 方法一:使用 as 断言(简单粗暴)

告诉 TS:"相信我,这些 key 一定是这个对象的 Key"。

typescript 复制代码
const user = { name: "Alice", age: 25 };

// 将 string[] 断言为 (keyof typeof user)[]
const keys = Object.keys(user) as Array<keyof typeof user>;

keys.forEach((key) => {
  console.log(key); // "name" | "age"
  console.log(user[key]); // ✅ 类型安全!自动推断为 string | number
});

解析

  • typeof user:获取变量 user 的类型 { name: string; age: number }
  • keyof typeof user:提取该类型的键联合类型 "name" | "age"
  • Array<...>:将其包裹为数组类型。

✅ 方法二:封装通用辅助函数(推荐复用)

为了避免每次都要写 as,可以封装一个类型安全的 helper 函数。

typescript 复制代码
function getObjectKeys<T extends object>(obj: T): (keyof T)[] {
  return Object.keys(obj) as (keyof T)[];
}

// 使用
const keys = getObjectKeys(user);
keys.forEach((key) => {
  // key 的类型自动推断为 "name" | "age"
  console.log(user[key]);
});

2. 🧩 编译时提取:keyof 操作符

有时候,我们不需要在运行时遍历,只需要在定义类型 时引用某个对象的 Key。这时 keyof 是神器。

✅ 场景 1:限制函数参数只能是对象的 Key

typescript 复制代码
interface User {
  id: number;
  name: string;
  email: string;
}

// 第二个参数必须是 User 的某个 key
function getUserProperty(user: User, key: keyof User) {
  return user[key];
}

getUserProperty({ id: 1, name: "Bob", email: "bob@test.com" }, "name"); // ✅ OK
getUserProperty({ id: 1, name: "Bob", email: "bob@test.com" }, "phone"); // ❌ Error: Argument of type '"phone"' is not assignable to parameter of type '"id" | "name" | "email"'.

✅ 场景 2:基于现有类型生成新类型

typescript 复制代码
type UserKeys = keyof User; // "id" | "name" | "email"

// 创建一个只包含部分属性的新类型
type PartialUser = Pick<User, "name" | "email">;

3. 🚀 高级玩法:泛型与工具类型结合

在编写通用库或复杂组件时,我们经常需要动态处理对象属性。

✅ 场景:实现一个通用的 omit 函数

移除对象中的指定字段,并保留剩余字段的类型。

typescript 复制代码
function omit<T extends object, K extends keyof T>(
  obj: T,
  keys: K[],
): Omit<T, K> {
  const result = { ...obj };
  keys.forEach((key) => {
    delete result[key];
  });
  return result as Omit<T, K>;
}

const user = { id: 1, name: "Alice", password: "123" };
const safeUser = omit(user, ["password"]);

// safeUser 的类型自动推断为: { id: number; name: string; }
// password 属性已从类型中移除,访问 safeUser.password 会报错 ✅

✅ 场景:映射类型(Mapped Types)

将所有属性变为可选,或修改属性类型。

typescript 复制代码
// 将 User 的所有属性值变为 string
type StringifiedUser = {
  [K in keyof User]: string;
};
// 结果等价于: { id: string; name: string; email: string; }

4. ⚠️ 常见陷阱:索引签名与可选属性

❌ 陷阱 1:索引签名 [key: string]: any

如果对象定义了索引签名,keyof 的结果会变成 string | number,而不是具体的 Key 列表。

typescript 复制代码
interface Config {
  [key: string]: any; // 索引签名
  theme: string;
}

type ConfigKeys = keyof Config; // 结果是 string | number,而不是 "theme"

原因 :因为你可以用任意字符串访问 config["anything"],所以 TS 认为 Key 可以是任意字符串。

❌ 陷阱 2:可选属性 ?

keyof 依然会包含可选属性的 Key,但访问时需要注意 undefined

typescript 复制代码
interface User {
  name: string;
  age?: number;
}

type Keys = keyof User; // "name" | "age"

function getVal(u: User, k: Keys) {
  const val = u[k];
  // val 的类型是 string | number | undefined
  // 因为 age 可能是 undefined
}

❌ 陷阱 3:Object.keys 不包含原型链上的属性

Object.keys() 只返回对象自身 的可枚举属性。如果属性在原型链上,或者被设置为 enumerable: false,它将不会被获取到。这在大多数业务场景下是符合预期的,但需知晓。


5. 💡 总结

需求场景 推荐方案 关键点
运行时遍历对象 Object.keys(obj) as (keyof typeof obj)[] 使用断言修复类型
封装通用工具 function getKeys<T>(obj: T): (keyof T)[] 利用泛型复用逻辑
限制参数范围 keyof InterfaceName 编译时类型约束
类型转换/映射 [K in keyof T]: ... 映射类型语法

🚀 博主寄语

TypeScript 的强大之处在于类型与值的同步

单纯使用 Object.keys 只是 JS 的思维,而结合 keyoftypeof 才是 TS 的正道。

记住口诀

运行遍历用 Keys,

断言 keyof 保类型。

编译约束 keyof 出,

泛型配合更无敌。

索引签名需谨慎,

具体键名才清晰。

希望这篇文档能帮你彻底搞懂 TS 中获取对象 Key 的技巧!如果有疑问,欢迎在评论区留言。👇

喜欢这篇文章吗?记得点赞、收藏、转发哦! ❤️

相关推荐
呆萌很1 小时前
联想小新 (XiaoXin 16 IRL8) 笔记本安装Ubuntu系统
ubuntu
ZC跨境爬虫1 小时前
跟着 MDN 学 HTML day_54:(深入掌握 XSLTProcessor API)
前端·javascript·ui·html·媒体
Delicate2 小时前
JavaScript 的双面人生:基本类型与引用类型的那些事儿
javascript
YAwu112 小时前
JavaScript 作用域与执行机制深度解析
前端·javascript
Yue1682 小时前
天津理工大学前端组大一末期考核随记(2)
前端·javascript
程序员mine2 小时前
我开发了一个 Obsidian 插件「带宠物的锁屏」—— 保护我的笔记隐私
typescript
皓月盈江2 小时前
Linux Ubuntu系统使用Docker搭建vulhub靶场环境
linux·ubuntu·docker·tomcat·vulhub·漏洞靶场
w_t_y_y3 小时前
vue父子组件通信(二)祖先调用inject
前端·javascript·vue.js
wkj0013 小时前
JavaScript模块化技术进程详解
开发语言·javascript·ecmascript