《Go 后端开发者学 TypeScript:keyof关键字》

一句话

keyof T 取一个"类型"的所有属性名,得到一个字符串联合类型。

ini 复制代码
type User = {
  id: number;
  name: string;
  email: string;
};

type UserKeys = keyof User;   // "id" | "name" | "email"

注意:keyof 操作的是"类型",不是变量 。你不能 keyof user(实例),只能 keyof User(类型),或者 keyof typeof user。

三个最高频的用法

① 泛型约束:安全地按属性名取值(最常用)

vbnet 复制代码
function get<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const u: User = { id: 1, name: "张三", email: "a@b.com" };

const id = get(u, "id");      // 类型是 number ✅
const name = get(u, "name");  // 类型是 string ✅
get(u, "xxx");                // ❌ 报错:"xxx" 不在 keyof User 里

这就是 K extends keyof T + 索引访问 T[K] 的组合:

  • K extends keyof T → 限制 key 只能是 T 的合法属性名;
  • T[K] → 自动推导出该属性的值类型 (传 "id" 得到 number,传 "name" 得到 string)。

这几乎是所有"类型安全对象操作"的模板 ------lodash.get、pick、omit 的 TS 实现都靠它。

② 映射类型:批量改造属性(Partial/Pick 的底层)

scala 复制代码
// 手写一个 Partial:把所有属性变可选
type MyPartial<T> = {
  [K in keyof T]?: T[K];
};

type PartialUser = MyPartial<User>;
// { id?: number; name?: string; email?: string }

// 手写一个 Pick:只挑几个属性
type MyPick<T, K extends keyof T> = {
  [P in K]: T[P];
};

type IdAndName = MyPick<User, "id" | "name">;
// { id: number; name: string }

{ [K in keyof T]: ... } 就是"遍历 T 的每个键",这是 TS 工具类型(Partial/Required/Readonly/Pick/Omit/Record)的共同骨架。

③ keyof typeof:从"值"反推键

ini 复制代码
const config = {
  host: "localhost",
  port: 8080,
};

type ConfigKey = keyof typeof config;   // "host" | "port"
  • typeof config → 把值变成类型 { host: string; port: number };
  • keyof → 再取键 → "host" | "port"。

这个组合在实际项目里很常见:定义了一个常量对象,想拿到它的键当联合类型用。

几个细节/坑

① 可选属性也会被算进 keyof

ini 复制代码
type A = { id?: number };
type K = keyof A;   // "id"(可选不影响取键)

② 索引签名会得到 string | number

ini 复制代码
type Dict = { [key: string]: number };
type K = keyof Dict;   // string | number

③ 数组/对象的 keyof 是"方法名"

lua 复制代码
type K = keyof string[];   // number | "length" | "push" | "pop" | ...

④ keyof any = string | number | symbol

ini 复制代码
type AnyKey = keyof any;   // string | number | symbol

对照 Go

Go 没有 keyof 这种"编译期取结构体字段名"的能力。最接近的是运行时反射:

css 复制代码
// Go:运行时拿字段名
t := reflect.TypeOf(User{})
field, _ := t.FieldByName("Name")

差别在于:

  • Go 的 reflect 是运行时、有性能开销、靠字符串匹配;
  • TS 的 keyof 是编译期类型运算 ,零运行时成本,还能让编译器提前报错(get(u, "xxx") 直接编译不过)。

TS 的 keyof 相当于"把 Go 反射的字段名检查,提前到编译期" 。

一句话总结

keyof T = T 的属性名联合;K extends keyof T + T[K] = 类型安全的"按键取值";[K in keyof T] = 遍历属性批量改造。 记住这三个组合,TS 工具类型你就能自己写了。

相关推荐
Penge66625 分钟前
《Go 后端开发者学 TypeScript:extends关键字》
javascript
张张123y32 分钟前
DSH 插件调试避坑:client.js 设置面板、热重载与高频报错排查表
开发语言·javascript·人工智能·开源·ecmascript·后端开发·ai agent
Penge6666 小时前
《Go 后端开发者学 TypeScript:typescript 类型断言》
javascript
Penge6666 小时前
《Go 后端开发者学 TypeScript:Symbol》
javascript
三*一8 小时前
Mapbox‑GL JS 多边形折点编辑工具:实现原理、功能拆解与踩坑总结
开发语言·javascript·ecmascript
FungLeo9 小时前
成为全栈·基础补充·Node.js 运行时:事件循环与异步
javascript·node.js·事件循环·后端开发·异步编程·成为全栈
Penge6669 小时前
《Go 后端开发者学 TypeScript:类型别名》
javascript
Penge66617 小时前
《Go 后端开发者学 TypeScript:数组和元组》
javascript
Penge66617 小时前
《Go 后端开发者学 TypeScript:export关键字大全》
javascript