keyofTypeScript 的类型操作符

名称 来源 说明
keyof TypeScript 类型操作符
TypeScript Microsoft JavaScript 的超集
关系 TypeScript 的一部分 编译时类型操作

🎯 核心作用

获取对象类型所有键的 联合类型

💡 基本用法

// 定义对象类型
interface User {
  id: number
  name: string
  age: number
  email: string
}

// 使用 keyof
type UserKeys = keyof User
// UserKeys = "id" | "name" | "age" | "email"

🔧 实际示例

1. 获取对象所有键

interface Device {
  id: string
  name: string
  type: 'sensor' | 'controller'
  status: 'online' | 'offline' | 'error'
  location: string
}

// 获取所有键
type DeviceKeys = keyof Device
// DeviceKeys = "id" | "name" | "type" | "status" | "location"

2. 动态访问对象属性

// 安全的属性访问函数
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]  // 类型安全
}

const device: Device = { /* ... */ }

// ✅ 正确
getProperty(device, 'name')  // string
getProperty(device, 'status')  // 'online' | 'offline' | 'error'

// ❌ TypeScript 报错
getProperty(device, 'color')  // Device 没有 color 属性

🔄 编译时 vs 运行时

TypeScript 源码

// 编译时类型操作
type Keys = keyof Device
// Keys = "id" | "name" | "type" | "status" | "location"

编译为 JavaScript

// keyof 在编译时被移除
// 运行时没有 keyof
// 只留下 JavaScript 代码

🆚 与 JavaScript 的 Object.keys 对比

特性 TypeScript keyof JavaScript Object.keys()
时机 编译时 运行时
结果 类型 字符串数组
用途 类型检查 实际数据操作
错误检查 编译时报错 运行时可能出错

对比示例

interface User {
  id: number
  name: string
}

// TypeScript: 编译时类型
type UserKeyType = keyof User  // "id" | "name"

// JavaScript: 运行时数据
const user = { id: 1, name: 'Alice' }
const userKeys = Object.keys(user)  // ["id", "name"]

💡 高级用法

1. 与泛型结合

// 通用的更新函数
function updateField<T, K extends keyof T>(
  obj: T,
  field: K,
  value: T[K]
): T {
  return { ...obj, [field]: value }
}

const device: Device = { /* ... */ }

// ✅ 类型安全
updateField(device, 'name', '新设备名')
updateField(device, 'status', 'online')

// ❌ 错误
updateField(device, 'name', 123)  // name 应该是 string
updateField(device, 'color', 'red')  // 没有 color 字段

2. 映射类型

// 将对象所有属性变为可选
type Partial<T> = {
  [K in keyof T]?: T[K]
}

// 将对象所有属性变为只读
type Readonly<T> = {
  readonly [K in keyof T]: T[K]
}

3. 条件类型

// 提取特定类型的属性键
type StringKeys<T> = {
  [K in keyof T]: T[K] extends string ? K : never
}[keyof T]

interface User {
  id: number
  name: string
  email: string
  age: number
}

type UserStringKeys = StringKeys<User>
// UserStringKeys = "name" | "email"

TypeScript 版本兼容

TypeScript 版本 keyof 特性
2.1+ 基本 keyof
2.9+ keyof 支持 number 和 symbol 键
3.5+ keyof 改进联合类型
4.1+ 模板字面量类型

🎯 为什么重要?

1. 类型安全

// 避免拼写错误
const field: keyof Device = 'name'  // ✅
const field: keyof Device = 'naem'  // ❌ TypeScript 报错

2. 自动重构

// 重命名属性时,所有 keyof 引用自动更新
interface User {
  username: string  // 从 name 重命名为 username
  // ...
}

// 所有 keyof User 的地方自动从 "name" 变为 "username"

3. 更好的开发体验

// IDE 自动补全
const key: keyof Device = ''  // 输入 '' 时提示所有键
// 提示: "id" | "name" | "type" | "status" | "location"

📊 常见使用模式

模式 示例 用途
Record<keyof T, U> Record<keyof T, boolean> 创建同结构的对象
K extends keyof T function get<T, K extends keyof T> 泛型约束
T[K] type ValueType = T[K] 获取属性值类型
Partial Partial<Record<keyof T, U>> 部分属性

📋 总结

keyof 是 TypeScript 的核心类型操作符

  1. ✅ 类型操作:获取对象类型的键的联合类型
  2. ✅ 编译时:只在 TypeScript 编译阶段起作用
  3. ✅ 类型安全:确保属性访问的安全性
  4. ✅ 工具基础:是许多高级类型工具的基础
  5. ✅ 开发效率:提供更好的 IDE 支持和重构能力