基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

keyof 关键字的作用

知识点图片

TypeScript 中的 keyof 是一个索引类型查询操作符(Index Type Query Operator)。

简单来说,它的作用是获取一个对象类型(Interface 或 Type)的所有键(Key),并将它们组合成一个联合类型(Union Type)。

以下是关于 keyof 的详细解析和常见用法:

1. 基本用法

当你对一个对象类型使用 keyof 时,它会返回该对象所有属性名组成的字符串字面量联合类型。

typescript
interface User {
  id: number;
  name: string;
  email: string;
}

// UserKeys 的类型变成了: "id" | "name" | "email"
type UserKeys = keyof User;

// 测试
const key1: UserKeys = "name";  // ✅ 合法
const key2: UserKeys = "age";   // ❌ 报错:Type '"age"' is not assignable to type 'keyof User'.

2. 最核心的应用场景:泛型约束 (Generics Constraints)

keyof 最常见且最强大的用法是配合泛型使用,用于安全地访问对象属性

假设我们要写一个函数,获取对象中某个属性的值。如果没有 keyof,TypeScript 无法知道你传入的 key 是否真的存在于对象中。

不使用 keyof (不安全/报错):

typescript
function getProperty(obj: any, key: string) {
  return obj[key]; // 返回类型是 any,失去了类型检查
}

使用 keyof (类型安全):
我们使用 K extends keyof T 来告诉 TypeScript:K 必须是 T 的键之一。

typescript
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = {
  name: "Alice",
  age: 30
};

// ✅ 合法:TS 知道 "name" 存在,且返回值自动推导为 string
const userName = getProperty(user, "name"); 

// ✅ 合法:TS 知道 "age" 存在,且返回值自动推导为 number
const userAge = getProperty(user, "age");

// ❌ 报错:Argument of type '"address"' is not assignable to parameter of type '"name" | "age"'.
getProperty(user, "address"); 

优点:

  1. 代码提示:当你输入第二个参数时,IDE 会自动列出 user 的所有属性。
  2. 类型检查:如果你拼写错误或访问不存在的属性,编译时就会报错。
  3. 返回值推导:TypeScript 知道 user['age'] 一定是 number 类型。

3. 处理索引签名 (Index Signatures)

如果一个类型定义了索引签名(例如数组或字典),keyof 的行为会有所不同。

数字索引签名:

typescript
interface StringArray {
  [index: number]: string;
}

// keyof StringArray 是 number
type K = keyof StringArray; 

字符串索引签名:

typescript
interface StringMap {
  [key: string]: unknown;
}

// keyof StringMap 是 string | number
type M = keyof StringMap;

注意: 为什么是 string | number?因为在 JavaScript 中,obj[0]obj["0"] 是等价的,数字键会被强制转换为字符串,所以 TypeScript 为了兼容性,允许你用 number 去索引字符串签名的对象。

4. 在映射类型 (Mapped Types) 中的应用

keyof 常用于创建新的类型,基于旧类型的键进行遍历。例如 TypeScript 内置的 Partial(将所有属性变可选)就是这么实现的:

typescript
// 将 T 中的所有属性 P 变为可选
type MyPartial<T> = {
  [P in keyof T]?: T[P];
};

interface Todo {
  title: string;
  description: string;
}

// PartialTodo 类型为 { title?: string; description?: string; }
type PartialTodo = MyPartial<Todo>;

总结

keyof 关键字的主要作用是:

  1. 提取键名:生成对象属性名的联合类型。
  2. 类型约束:确保在访问对象属性时,使用的键是真实存在的(K extends keyof T)。
  3. 智能推导:让 IDE 提供更好的自动补全和类型推断。
00:00
00:00