javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > TypeScript获取对象所有Key

TypeScript精准获取对象的所有Key的方法

作者:yqcoder

在 JavaScript 中,Object.keys(obj) 返回一个字符串数组,这没问题,但在 TypeScript 中,如果你直接用它来访问对象属性,编译器会报错,我们需要的是:既拿到运行时的 Key 值,又保留编译时的类型信息,所以本文给大家介绍了TypeScript精准获取对象的所有Key的方法

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

在 JavaScript 中,Object.keys(obj) 返回一个字符串数组,这没问题。但在 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 返回 string[] 的问题。

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

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

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
});

解析

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

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

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

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:基于现有类型生成新类型

type UserKeys = keyof User; // "id" | "name" | "email"

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

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

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

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

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

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)

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

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

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

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

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

interface Config {
  [key: string]: any; // 索引签名
  theme: string;
}

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

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

陷阱 2:可选属性?

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

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 的技巧!

以上就是TypeScript精准获取对象的所有Key的方法的详细内容,更多关于TypeScript获取对象所有Key的资料请关注脚本之家其它相关文章!

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