keyof、typeof、索引访问类型到了这一篇TypeScript 会开始呈现出它真正“像一门类型编程语言”的味道。前面你主要是在手写类型结构而这一篇开始你会发现类型信息不一定都靠手写它还可以从已有值中提取、从已有类型中索引、再继续组合推导。这一步非常关键。因为现代 TypeScript 工程写法里大量高质量类型设计都建立在一个思路上尽量减少重复让值和类型之间保持同步。而keyof、typeof、索引访问类型正是这套思路的核心工具。keyof拿到一个对象类型的键集合先看一个最基础的例子interfaceUser{id:number;name:string;active:boolean;}typeUserKeykeyofUser;此时UserKey的结果是id|name|active也就是说keyof会把一个对象类型的键提取成联合类型。这件事看似简单但实际非常有价值。因为你终于能表达“这里只允许传对象中真实存在的字段名”而不是一个宽泛的string。一个典型用途限制键名输入functiongetValue(user:User,key:keyofUser){returnuser[key];}现在调用时getValue(user,name);是合法的但getValue(user,email);就会直接报错。这类约束在表单字段读取、排序字段选择、配置键名传递、表格列定义里都很常见。你能明显感受到keyof不是为了让语法更复杂而是为了把“合法键”这件事真正交给类型系统检查。typeof从值反推出类型在 JavaScript 里typeof是运行时运算符在 TypeScript 的类型上下文里typeof可以把一个已有值的类型提取出来。constconfig{apiBase:/api,timeout:5000};typeConfigtypeofconfig;这里的Config等价于{apiBase:string;timeout:number;}这个能力非常实用尤其在这些场景里配置对象常量映射表路由定义权限字典状态常量它能避免你一边写运行时值一边手写一份重复类型。从“手写两份”到“值和类型共享一份来源”这是typeof最值得学的地方。很多初学者一开始会这样写typeConfig{apiBase:string;timeout:number;};constconfig:Config{apiBase:/api,timeout:5000};这当然没错。但如果很多场景里值本身已经是事实来源那么反过来从值推导类型往往更省重复constconfig{apiBase:/api,timeout:5000};typeConfigtypeofconfig;这种思路在大型代码库里很有价值因为它减少了“改了值却忘了同步改类型”的机会。索引访问类型从一个类型里拿出局部信息typeUserNameUser[name];这里UserName会得到string。这就叫索引访问类型。你可以把它理解成“在类型层面做属性访问”。它不止能取单个属性typeUserValueUser[keyofUser];这表示User所有属性值类型的联合也就是number|string|boolean到了这里你应该开始感受到一个重要变化TypeScript 类型系统正在变得更像一种可组合、可推导的语言而不是简单注解。三者组合起来才是最有威力的地方单独看keyof、typeof、索引访问类型都不算难。真正让它们变强的是组合。看一个经典工具函数functionpickT,KextendskeyofT(obj:T,key:K):T[K]{returnobj[key];}这段代码同时用到了T对象类型K extends keyof T键必须来自对象真实键集合T[K]返回值自动对应当前键的属性类型调用时constuser{id:1,name:Alice};constnamepick(user,name);此时name会被推断成string而不是any也不是string | number。这类写法是你后面看很多工具库源码时的基础。as const会让这套能力更精确看下面这个例子constSTATUS{PENDING:pending,DONE:done}asconst;typeStatustypeofSTATUS[keyoftypeofSTATUS];为什么这里要加as const因为如果不加TypeScript 可能会把值推宽成string加上之后值会被锁定为字面量类型pending和done。于是Status最终得到的不是普通string而是pending|done这正是现代 TypeScript 中“对象常量 字面量联合”这种模式的基础。一个特别实用的工程模式假设你有一组表单字段定义constfields{username:用户名,email:邮箱,phone:手机号}asconst;typeFieldNamekeyoftypeoffields;现在FieldName自动变成username|email|phone你后面无论写表单校验器、错误对象、字段权限配置都可以直接围绕这一份定义衍生而不用手写第二份字段联合类型。为什么这一步会让你感觉 TypeScript 突然变强了因为你开始进入一种新的写法不再机械地手写所有类型而是让类型系统从已有结构中“拿信息”“做推导”“自动保持同步”。这背后的思路非常重要用值定义事实用typeof从事实中提取类型用keyof获取合法键集合用索引访问类型提取局部类型一旦你习惯这套思路后面的映射类型、工具类型、条件类型都会顺很多。常见误区误区一keyof只是拿个键名感觉没什么用单独看确实简单但一旦和泛型、索引访问类型结合它就能让工具函数变得非常精确。误区二typeof只会拿到宽泛类型如果你配合as const使用typeof的结果会精确很多尤其适合常量映射表。误区三值和类型各写一套这在小项目里问题不大但在长期维护中很容易不同步。能共用来源时尽量别手写两份事实。本文小结keyof让你获得对象的合法键集合typeof让你从已有值中提取类型索引访问类型则让你可以进一步从类型中拿出局部结构。它们组合起来构成了 TypeScript 中非常核心的一种写法让值和类型互相联动减少重复提升精确度。你如果能真正掌握这一层后面再看Partial、Pick、Record、条件类型这些工具就不会觉得它们是凭空出现的魔法而会知道它们其实都建立在同样的推导思路之上。练习定义一个Settings接口用keyof拿到所有键并将它用于一个只允许传合法配置项名称的函数。定义一个配置对象用typeof推导它的类型再尝试修改值结构并观察类型如何同步变化。写一个泛型函数只允许读取对象中存在的键并让返回值类型自动跟随键变化。后记2026年5月21日于上海。
【Typescript】08-keyof-typeof-索引访问类型
keyof、typeof、索引访问类型到了这一篇TypeScript 会开始呈现出它真正“像一门类型编程语言”的味道。前面你主要是在手写类型结构而这一篇开始你会发现类型信息不一定都靠手写它还可以从已有值中提取、从已有类型中索引、再继续组合推导。这一步非常关键。因为现代 TypeScript 工程写法里大量高质量类型设计都建立在一个思路上尽量减少重复让值和类型之间保持同步。而keyof、typeof、索引访问类型正是这套思路的核心工具。keyof拿到一个对象类型的键集合先看一个最基础的例子interfaceUser{id:number;name:string;active:boolean;}typeUserKeykeyofUser;此时UserKey的结果是id|name|active也就是说keyof会把一个对象类型的键提取成联合类型。这件事看似简单但实际非常有价值。因为你终于能表达“这里只允许传对象中真实存在的字段名”而不是一个宽泛的string。一个典型用途限制键名输入functiongetValue(user:User,key:keyofUser){returnuser[key];}现在调用时getValue(user,name);是合法的但getValue(user,email);就会直接报错。这类约束在表单字段读取、排序字段选择、配置键名传递、表格列定义里都很常见。你能明显感受到keyof不是为了让语法更复杂而是为了把“合法键”这件事真正交给类型系统检查。typeof从值反推出类型在 JavaScript 里typeof是运行时运算符在 TypeScript 的类型上下文里typeof可以把一个已有值的类型提取出来。constconfig{apiBase:/api,timeout:5000};typeConfigtypeofconfig;这里的Config等价于{apiBase:string;timeout:number;}这个能力非常实用尤其在这些场景里配置对象常量映射表路由定义权限字典状态常量它能避免你一边写运行时值一边手写一份重复类型。从“手写两份”到“值和类型共享一份来源”这是typeof最值得学的地方。很多初学者一开始会这样写typeConfig{apiBase:string;timeout:number;};constconfig:Config{apiBase:/api,timeout:5000};这当然没错。但如果很多场景里值本身已经是事实来源那么反过来从值推导类型往往更省重复constconfig{apiBase:/api,timeout:5000};typeConfigtypeofconfig;这种思路在大型代码库里很有价值因为它减少了“改了值却忘了同步改类型”的机会。索引访问类型从一个类型里拿出局部信息typeUserNameUser[name];这里UserName会得到string。这就叫索引访问类型。你可以把它理解成“在类型层面做属性访问”。它不止能取单个属性typeUserValueUser[keyofUser];这表示User所有属性值类型的联合也就是number|string|boolean到了这里你应该开始感受到一个重要变化TypeScript 类型系统正在变得更像一种可组合、可推导的语言而不是简单注解。三者组合起来才是最有威力的地方单独看keyof、typeof、索引访问类型都不算难。真正让它们变强的是组合。看一个经典工具函数functionpickT,KextendskeyofT(obj:T,key:K):T[K]{returnobj[key];}这段代码同时用到了T对象类型K extends keyof T键必须来自对象真实键集合T[K]返回值自动对应当前键的属性类型调用时constuser{id:1,name:Alice};constnamepick(user,name);此时name会被推断成string而不是any也不是string | number。这类写法是你后面看很多工具库源码时的基础。as const会让这套能力更精确看下面这个例子constSTATUS{PENDING:pending,DONE:done}asconst;typeStatustypeofSTATUS[keyoftypeofSTATUS];为什么这里要加as const因为如果不加TypeScript 可能会把值推宽成string加上之后值会被锁定为字面量类型pending和done。于是Status最终得到的不是普通string而是pending|done这正是现代 TypeScript 中“对象常量 字面量联合”这种模式的基础。一个特别实用的工程模式假设你有一组表单字段定义constfields{username:用户名,email:邮箱,phone:手机号}asconst;typeFieldNamekeyoftypeoffields;现在FieldName自动变成username|email|phone你后面无论写表单校验器、错误对象、字段权限配置都可以直接围绕这一份定义衍生而不用手写第二份字段联合类型。为什么这一步会让你感觉 TypeScript 突然变强了因为你开始进入一种新的写法不再机械地手写所有类型而是让类型系统从已有结构中“拿信息”“做推导”“自动保持同步”。这背后的思路非常重要用值定义事实用typeof从事实中提取类型用keyof获取合法键集合用索引访问类型提取局部类型一旦你习惯这套思路后面的映射类型、工具类型、条件类型都会顺很多。常见误区误区一keyof只是拿个键名感觉没什么用单独看确实简单但一旦和泛型、索引访问类型结合它就能让工具函数变得非常精确。误区二typeof只会拿到宽泛类型如果你配合as const使用typeof的结果会精确很多尤其适合常量映射表。误区三值和类型各写一套这在小项目里问题不大但在长期维护中很容易不同步。能共用来源时尽量别手写两份事实。本文小结keyof让你获得对象的合法键集合typeof让你从已有值中提取类型索引访问类型则让你可以进一步从类型中拿出局部结构。它们组合起来构成了 TypeScript 中非常核心的一种写法让值和类型互相联动减少重复提升精确度。你如果能真正掌握这一层后面再看Partial、Pick、Record、条件类型这些工具就不会觉得它们是凭空出现的魔法而会知道它们其实都建立在同样的推导思路之上。练习定义一个Settings接口用keyof拿到所有键并将它用于一个只允许传合法配置项名称的函数。定义一个配置对象用typeof推导它的类型再尝试修改值结构并观察类型如何同步变化。写一个泛型函数只允许读取对象中存在的键并让返回值类型自动跟随键变化。后记2026年5月21日于上海。