在 TypeScript 中,keyof
和 in
是用于类型操作的两个关键工具,常用于处理对象类型的键和属性映射。以下是它们的详细解释和用法示例:
1. keyof
关键字
作用:将对象类型的键提取为联合类型。
语法:keyof T
,其中 T
是对象类型。
结果:返回 T
的所有键的联合类型。
示例:
interface Person {name: string;age: number;
}type PersonKeys = keyof Person; // "name" | "age"
典型场景:
- 泛型约束:限制函数参数必须为对象的有效键。
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {return obj[key]; }
- 映射类型的基础:结合
in
使用,生成新类型(如Partial<T>
)。
特殊情况:
- 空对象:
keyof {}
结果为never
。 - 原始类型:
keyof string
返回String
包装对象的属性和方法的联合类型(如"length" | "charAt" | ...
)。
2. in
关键字
作用:在映射类型中遍历联合类型的每个成员。
语法:[K in T]
,其中 T
是联合类型。
结果:为联合类型中的每个成员生成新的属性。
示例:
type Keys = "a" | "b";
type MyObject = {[K in Keys]: string;
};
// 等价于 { a: string; b: string; }
典型场景:
- 工具类型的实现:如
Partial<T>
、Readonly<T>
。type Partial<T> = {[P in keyof T]?: T[P]; };
- 动态生成类型:根据联合类型创建新对象类型。
3. keyof
和 in
的协作
两者常结合使用,keyof
提供键的联合类型,in
遍历这些键生成新属性。
示例:
interface Config {id: number;path: string;
}type ConfigGetters = {[K in keyof Config as `get${Capitalize<K>}`]: () => Config[K];
};
// 结果:
// {
// getId: () => number;
// getPath: () => string;
// }
4. 其他注意事项
- 索引签名:
若对象有索引签名,keyof
会返回索引类型。interface StringMap {[key: string]: number; } type Keys = keyof StringMap; // string | number
- 枚举类型:
使用keyof typeof Enum
获取枚举的键。enum Direction { Up, Down } type DirectionKeys = keyof typeof Direction; // "Up" | "Down"
- 类型保护:
in
在值空间用于检查属性存在(JavaScript 特性):if ("name" in obj) {// 类型收窄为包含 name 属性的类型 }
总结
关键字 | 作用场景 | 示例 | 结果 |
---|---|---|---|
keyof | 提取对象键的联合类型 | keyof { a: 1, b: 2 } | "a" | "b" |
in | 遍历联合类型生成属性 | { [K in "x"|"y"]: number } | { x: number; y: number } |
掌握 keyof
和 in
的使用,能显著提升 TypeScript 类型操作的灵活性和类型安全性。