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 的思维,而结合 keyof 和 typeof 才是 TS 的正道。

记住口诀:
运行遍历用 Keys,
断言 keyof 保类型。
编译约束 keyof 出,
泛型配合更无敌。
索引签名需谨慎,
具体键名才清晰。

希望这篇文档能帮你彻底搞懂 TS 中获取对象 Key 的技巧!

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

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