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");
优点:
- 代码提示:当你输入第二个参数时,IDE 会自动列出
user的所有属性。 - 类型检查:如果你拼写错误或访问不存在的属性,编译时就会报错。
- 返回值推导: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 关键字的主要作用是:
- 提取键名:生成对象属性名的联合类型。
- 类型约束:确保在访问对象属性时,使用的键是真实存在的(
K extends keyof T)。 - 智能推导:让 IDE 提供更好的自动补全和类型推断。