当前位置: 首页 > news >正文

TypeScript 类型体操实战:从看不懂到手撕 5 道高频面试题

上周面试被一道 TypeScript 类型题干懵了。面试官让我手写一个DeepReadonly<T>,我愣了十秒钟,脑子里全是as any的影子。回来之后花了三天把 type-challenges 刷了一遍,发现类型体操这东西,核心套路就那几个,搞懂之后真的是降维打击。

今天把我总结的 5 道高频类型体操题整理出来,每道题都有完整思路和可运行代码。准备面试或者想提升项目类型安全的,应该都能用上。

先说结论

类型体操的核心武器就四把刀:

  • 条件类型T extends U ? X : Y—— 类型的 if-else
  • infer 关键字—— 在条件类型里做模式匹配,提取你要的部分
  • 映射类型{ [K in keyof T]: ... }—— 遍历对象的每个 key
  • 递归—— 处理嵌套结构,和写函数递归一个思路

掌握这四个就能解决 80% 的题目,剩下 20% 靠模板字面量类型和一些奇技淫巧。

类型体操四大核心

条件类型 extends

infer 模式匹配

映射类型 keyof + in

递归类型

联合类型分发

提取函数参数/返回值

批量修改属性修饰符

处理嵌套对象/数组

实战:Exclude / Extract

第一题:MyPick<T, K>

经典开胃菜,实现内置的Pick

用映射类型遍历 K 中的每个 key,从 T 中取对应的值类型。

typeMyPick<T,KextendskeyofT>={[PinK]:T[P]}// 测试interfaceTodo{title:stringdescription:stringcompleted:boolean}typeTodoPreview=MyPick<Todo,'title'|'completed'>// 结果: { title: string; completed: boolean }consttodo:TodoPreview={title:'刷类型体操',completed:false,}

这题本身不难,关键是理解K extends keyof T这个约束——它限制了 K 只能是 T 的 key 的子集。面试时别忘加这个约束,不然扣分。

第二题:MyReturnType

实现内置的ReturnType,提取函数的返回值类型。这题是infer的入门必做题。

用条件类型判断 T 是不是函数类型,如果是就用infer R把返回值类型"抠"出来。

typeMyReturnType<Textends(...args:any[])=>any>=Textends(...args:any[])=>inferR?R:never// 测试constfn=(a:number,b:string)=>({a,b,c:true})typeResult=MyReturnType<typeoffn>// 结果: { a: number; b: string; c: boolean }typeStrResult=MyReturnType<()=>string>// 结果: string

infer可以理解成正则里的捕获组。infer R就是说"这个位置是什么类型我不知道,你帮我推断出来,我管它叫 R"。理解了这个,后面的题就好办了。

第三题:DeepReadonly

就是面试干懵我的那题。把对象所有层级的属性都变成readonly

映射类型 + 递归。遍历每个 key,如果值是对象就递归处理,否则直接返回。

typeDeepReadonly<T>={readonly[KinkeyofT]:T[K]extendsobject?T[K]extendsFunction?T[K]// 函数类型不递归,直接返回:DeepReadonly<T[K]>// 对象类型递归处理:T[K]// 基本类型直接返回}// 测试interfaceNestedConfig{db:{host:stringport:numberoptions:{ssl:booleantimeout:number}}debug:booleancallback:()=>void}typeReadonlyConfig=DeepReadonly<NestedConfig>// 验证:以下赋值会报错constconfig:ReadonlyConfig={db:{host:'localhost',port:5432,options:{ssl:true,timeout:3000}},debug:false,callback:()=>console.log('ok'),}// config.debug = true // ❌ Error: Cannot assign to 'debug'// config.db.options.ssl = false // ❌ Error: Cannot assign to 'ssl'

这里有个坑:object类型在 TypeScript 里包含 Function、Array 等。所以要单独判断Function排除掉,不然函数属性会被当成普通对象递归展开,类型就炸了。这个细节面试时说出来,绝对加分。

第四题:TupleToUnion

把元组类型转成联合类型。比如[string, number, boolean]变成string | number | boolean

有个很骚的写法,直接用索引访问T[number]

// 方法一:索引访问(推荐,简洁)typeTupleToUnion<Textendsreadonlyany[]>=T[number]// 方法二:递归实现(面试可能会要求你写这种)typeTupleToUnionRecursive<Textendsreadonlyany[]>=Textends[inferFirst,...inferRest]?First|TupleToUnionRecursive<Rest>:never// 测试typeTuple=[string,number,boolean]typeUnion1=TupleToUnion<Tuple>// string | number | booleantypeUnion2=TupleToUnionRecursive<Tuple>// string | number | boolean// 实际使用场景:限制函数参数只能是特定值constallowedMethods=['GET','POST','PUT','DELETE']asconsttypeHttpMethod=TupleToUnion<typeofallowedMethods>// 结果: "GET" | "POST" | "PUT" | "DELETE"functionrequest(method:HttpMethod,url:string){console.log(method,url)}request('GET','/api/users')// ✅// request('PATCH', '/api/users') // ❌ 类型报错

T[number]的原理:元组用number做索引,TypeScript 会把所有位置的类型取出来合成联合类型。这个写法在实际项目里很好用,配合as const做枚举替代方案的时候尤其顺手。

第五题:Chainable

实现一个链式调用的类型。type-challenges 里的中等难度,出镜率极高。

typeChainable<T={}>={option<Kextendsstring,V>(key:KextendskeyofT?never:K,// key 不能重复value:V):Chainable<Omit<T,K>&Record<K,V>>// 合并到结果类型get():T}// 测试declareconstconfig:Chainableconstresult=config.option('name','typescript').option('age',26).option('isAdmin',true).get()// result 的类型自动推断为:// { name: string; age: number; isAdmin: boolean }// 以下会报错,因为 key 重复了:// config.option('name', 'ts').option('name', 123) // ❌

这题的精华在两处:

  1. K extends keyof T ? never : K阻止了重复 key。当 K 已经在 T 里存在时,类型变成never,传任何字符串都不满足never,所以报错。
  2. 返回Chainable<Omit<T, K> & Record<K, V>>,每次调用.option()都返回一个新的Chainable,类型参数不断积累。

这个模式在实际项目的 Builder Pattern 里很常见,ORM 查询构建器、配置对象构建器,都是这个套路。

踩坑记录

刷题过程中踩了几个坑,记一下。

坑 1:联合类型的分发行为

// 条件类型对联合类型会自动分发typeToArray<T>=Textendsany?T[]:nevertypeResult=ToArray<string|number>// 你以为是 (string | number)[]// 实际是 string[] | number[]// 要阻止分发,用元组包一层typeToArrayNoDistribute<T>=[T]extends[any]?T[]:nevertypeResult2=ToArrayNoDistribute<string|number>// 这回是 (string | number)[]

这个行为不知道的话,很多题的结果都对不上,我当时 debug 了好久。

坑 2:递归深度限制

TypeScript 的类型递归有深度限制(大概 50 层左右,不同版本有差异)。处理深层嵌套结构时可能会报Type instantiation is excessively deep and possibly infinite。实际项目里遇到这种情况,一般是类型设计有问题,需要换思路,不是硬递归能解决的。

坑 3:空对象{}不是你想的那样

// {} 在 TypeScript 里不是"空对象"// 它表示"任何非 null 非 undefined 的值"typeTest1=stringextends{}?true:false// truetypeTest2=numberextends{}?true:false// truetypeTest3=nullextends{}?true:false// false// 想表示真正的空对象,用 Record<string, never>

写类型工具的时候经常在这里栽跟头。

小结

类型体操说白了就是在类型层面写逻辑。把extends当 if-else,infer当变量解构,映射类型当 for 循环,递归就是递归——跟写普通函数的思维没什么两样。

我现在的习惯是每周刷两三道 type-challenges 保持手感,比背八股文有用多了。面试官看你手写类型的流畅度,比你说一堆"TypeScript 支持类型推导和类型保护"有说服力得多。

最后说句实话,2026 年了,有 Claude Code 和 Cursor 这些工具,项目里复杂的类型工具函数确实可以让 AI 帮你写初版。但理解原理还是必要的——AI 写的类型有时候过度复杂或者有边界 case 漏洞,你不懂原理根本 review 不出来。工具帮你提效,脑子帮你兜底,缺一不可。

http://www.jsqmd.com/news/524383/

相关文章:

  • 2024 2025-2026-2 《Python程序设计》实验1报告
  • Goreplay实战:如何用3条命令搞定生产环境流量复制到测试服务器
  • 20252417 2025-2026-2 《Python程序设计》实验1报告
  • c语言之时间函数操作
  • [INFRA] EMR集群MetricsCollector组件功能和运行原理分析
  • 2026年五恒系统厂家推荐排行榜:别墅/大平层/洋房/叠拼/独栋/豪宅全屋定制,专业打造恒温恒湿恒氧恒洁恒静舒适生活空间 - 品牌企业推荐师(官方)
  • C++初始化列表、类型转换
  • 解决Android Studio中annotation-experimental-1.4.1.aar版本冲突的实战指南
  • DeepSpeed多卡通信避坑指南:all_to_all_single的5个常见错误及解决方法
  • 20241223 实验一《Python程序设计》实验报告
  • AGV调度算法深度解析:从避碰优化到千车并行的技术演进
  • 混合动力汽车Simulink整车模型:探索P2并联混动仿真的奇妙世界
  • 嵌入式网络调试利器:在ARM开发板上手把手编译tcpdump 4.99.4
  • 算法复杂度理论的边界与不可计算性探讨的技术7
  • 2026会议音响套装优质品牌推荐指南:报告厅音响、无纸化会议室、无纸化会议终端、无纸化会议软件、无纸化办公系统选择指南 - 优质品牌商家
  • 168开奖网源码API修复记录
  • 6.1.1 软件->PEP标准(PSF基金会):Python 标准库标准(Python Standard Library Specification)
  • 基于LBM的Xflow单相及两相流动模拟探索
  • CrossEntropyLoss参数详解:从reduction=‘none‘到loss.backward()的完整避坑指南
  • 【C++面经】轻舟智航自动驾驶应用软件开发实习岗位
  • 五大品牌设计培训机构横评——后浪教育引领未来人才培养 - 速递信息
  • ComfyUI-WanVideoWrapper:AI视频创作者的技术赋能平台
  • 基于Java的OPC DA客户端开发与常见问题解析
  • Zynq开发避坑指南:FDMA读写AXI总线时最常见的3个时序错误
  • BurpSuite新手避坑大全:从安装到解决界面错位的5个关键步骤(2024.10版实测)
  • 数字电路入门:手把手教你理解RS触发器的核心原理(附避坑指南)
  • GPT-4o与Gemini 3镜像站背后的算力与工程:大模型训练基础设施拆解
  • 显卡调用精细化:1%算力+1MB显存代码方案
  • 佳易王小餐馆点餐管理系统软件功能观察与使用体验
  • Linux 系统安全实战:从服务防护到入侵检测