【Typescript】08-keyof-typeof-索引访问类型

keyoftypeof、索引访问类型

到了这一篇,TypeScript 会开始呈现出它真正"像一门类型编程语言"的味道。前面你主要是在手写类型结构,而这一篇开始,你会发现:类型信息不一定都靠手写,它还可以从已有值中提取、从已有类型中索引、再继续组合推导。

这一步非常关键。因为现代 TypeScript 工程写法里,大量高质量类型设计都建立在一个思路上:尽量减少重复,让值和类型之间保持同步。

keyoftypeof、索引访问类型,正是这套思路的核心工具。

keyof:拿到一个对象类型的键集合

先看一个最基础的例子:

ts 复制代码
interface User {
  id: number;
  name: string;
  active: boolean;
}

type UserKey = keyof User;

此时 UserKey 的结果是:

ts 复制代码
"id" | "name" | "active"

也就是说,keyof 会把一个对象类型的键,提取成联合类型。

这件事看似简单,但实际非常有价值。因为你终于能表达"这里只允许传对象中真实存在的字段名",而不是一个宽泛的 string

一个典型用途:限制键名输入

ts 复制代码
function getValue(user: User, key: keyof User) {
  return user[key];
}

现在调用时:

ts 复制代码
getValue(user, "name");

是合法的,但:

ts 复制代码
getValue(user, "email");

就会直接报错。

这类约束在表单字段读取、排序字段选择、配置键名传递、表格列定义里都很常见。你能明显感受到,keyof 不是为了让语法更复杂,而是为了把"合法键"这件事真正交给类型系统检查。

typeof:从值反推出类型

在 JavaScript 里,typeof 是运行时运算符;在 TypeScript 的类型上下文里,typeof 可以把一个已有值的类型提取出来。

ts 复制代码
const config = {
  apiBase: "/api",
  timeout: 5000
};

type Config = typeof config;

这里的 Config 等价于:

ts 复制代码
{
  apiBase: string;
  timeout: number;
}

这个能力非常实用,尤其在这些场景里:

  • 配置对象
  • 常量映射表
  • 路由定义
  • 权限字典
  • 状态常量

它能避免你一边写运行时值,一边手写一份重复类型。

从"手写两份"到"值和类型共享一份来源"

这是 typeof 最值得学的地方。很多初学者一开始会这样写:

ts 复制代码
type Config = {
  apiBase: string;
  timeout: number;
};

const config: Config = {
  apiBase: "/api",
  timeout: 5000
};

这当然没错。但如果很多场景里值本身已经是事实来源,那么反过来从值推导类型,往往更省重复:

ts 复制代码
const config = {
  apiBase: "/api",
  timeout: 5000
};

type Config = typeof config;

这种思路在大型代码库里很有价值,因为它减少了"改了值,却忘了同步改类型"的机会。

索引访问类型:从一个类型里拿出局部信息

ts 复制代码
type UserName = User["name"];

这里 UserName 会得到 string。这就叫索引访问类型。你可以把它理解成"在类型层面做属性访问"。

它不止能取单个属性:

ts 复制代码
type UserValue = User[keyof User];

这表示 User 所有属性值类型的联合,也就是:

ts 复制代码
number | string | boolean

到了这里,你应该开始感受到一个重要变化:TypeScript 类型系统正在变得更像一种可组合、可推导的语言,而不是简单注解。

三者组合起来,才是最有威力的地方

单独看 keyoftypeof、索引访问类型,都不算难。真正让它们变强的是组合。

看一个经典工具函数:

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

这段代码同时用到了:

  • T:对象类型
  • K extends keyof T:键必须来自对象真实键集合
  • T[K]:返回值自动对应当前键的属性类型

调用时:

ts 复制代码
const user = { id: 1, name: "Alice" };
const name = pick(user, "name");

此时 name 会被推断成 string,而不是 any,也不是 string | number

这类写法,是你后面看很多工具库源码时的基础。

as const 会让这套能力更精确

看下面这个例子:

ts 复制代码
const STATUS = {
  PENDING: "pending",
  DONE: "done"
} as const;

type Status = typeof STATUS[keyof typeof STATUS];

为什么这里要加 as const?因为如果不加,TypeScript 可能会把值推宽成 string;加上之后,值会被锁定为字面量类型 "pending""done"

于是 Status 最终得到的不是普通 string,而是:

ts 复制代码
"pending" | "done"

这正是现代 TypeScript 中"对象常量 + 字面量联合"这种模式的基础。

一个特别实用的工程模式

假设你有一组表单字段定义:

ts 复制代码
const fields = {
  username: "用户名",
  email: "邮箱",
  phone: "手机号"
} as const;

type FieldName = keyof typeof fields;

现在 FieldName 自动变成:

ts 复制代码
"username" | "email" | "phone"

你后面无论写表单校验器、错误对象、字段权限配置,都可以直接围绕这一份定义衍生,而不用手写第二份字段联合类型。

为什么这一步会让你感觉 TypeScript 突然变强了

因为你开始进入一种新的写法:不再机械地手写所有类型,而是让类型系统从已有结构中"拿信息""做推导""自动保持同步"。

这背后的思路非常重要:

  • 用值定义事实
  • typeof 从事实中提取类型
  • keyof 获取合法键集合
  • 用索引访问类型提取局部类型

一旦你习惯这套思路,后面的映射类型、工具类型、条件类型都会顺很多。

常见误区

误区一:keyof 只是拿个键名,感觉没什么用

单独看确实简单,但一旦和泛型、索引访问类型结合,它就能让工具函数变得非常精确。

误区二:typeof 只会拿到宽泛类型

如果你配合 as const 使用,typeof 的结果会精确很多,尤其适合常量映射表。

误区三:值和类型各写一套

这在小项目里问题不大,但在长期维护中很容易不同步。能共用来源时,尽量别手写两份事实。

本文小结

keyof 让你获得对象的合法键集合,typeof 让你从已有值中提取类型,索引访问类型则让你可以进一步从类型中拿出局部结构。它们组合起来,构成了 TypeScript 中非常核心的一种写法:让值和类型互相联动,减少重复,提升精确度。

你如果能真正掌握这一层,后面再看 PartialPickRecord、条件类型这些工具,就不会觉得它们是凭空出现的魔法,而会知道它们其实都建立在同样的推导思路之上。

练习

  1. 定义一个 Settings 接口,用 keyof 拿到所有键,并将它用于一个只允许传合法配置项名称的函数。
  2. 定义一个配置对象,用 typeof 推导它的类型,再尝试修改值结构并观察类型如何同步变化。
  3. 写一个泛型函数,只允许读取对象中存在的键,并让返回值类型自动跟随键变化。

后记

2026年5月21日于上海。

相关推荐
ShirleyWang01218 分钟前
Day02 K3s NGF(Nginx Gateway Fabric)单 Worker 环境网关更新与故障处置 SOP
linux·服务器·python·k8s·k3s
如此风景21 分钟前
TS装饰器详解
typescript
酷可达拉斯28 分钟前
Linux操作系统-shell编程(1)
linux·运维·服务器
j7~35 分钟前
【Linux】二十四.线程篇一《一文吃透Linux线程全面解析:概念、内存管理、优缺点与用途》
linux·运维·服务器·页表·线程概念·分页式存储管理·缺页异常
ShirleyWang0121 小时前
DAY01 K3s 私有化部署排障复盘与 SOP
linux·服务器·k8s·k3s·企业部署
AbandonForce1 小时前
Linux进程(WHAT HOW)
linux·运维·服务器
静默追光1 小时前
服务器——看门狗
linux·运维·服务器
无足鸟ICT1 小时前
【RHCA+】测试变量
linux
_abab2 小时前
Rust重塑系统编程:从Linux内核到AI推理引擎的2026全景解析
linux·人工智能·rust
初圣魔门首席弟子2 小时前
TypeScript 类型系统完全指南:从基础到高级工具类型(知识库版)
linux·运维·ubuntu