类型编程实战

共 59 题
📑 题目列表 59 题
#
★★★

1. noPropertyAccessFromIndexSignature 在 Monorepo 与项目引用的协作

noPropertyAccessFromIndexSignature 在 Monorepo 与项目引用场景中如何协作?

  • 该选项对索引签名属性访问的约束(点访问 vs 括号访问)
  • 与接口显式属性声明的引导
  • Monorepo 共享类型的一致性影响

noPropertyAccessFromIndexSignature 开启后,对"索引签名提供的属性"禁止点访问(obj.key),必须用括号访问(obj['key'])——因为点访问隐含"属性静态存在",而索引签名的键是动态的,强制括号访问表达"动态键"语义,并引导开发者给常用字段补显式接口声明。Monorepo 协作:共享包若用 Record/索引签名建模动态数据(配置表、事件载荷、API 字典),消费包在开启该选项后必须括号访问,类型语义统一;若共享包未开而消费包开启,跨包类型(从声明文件导出)仍按声明文件的编译选项检查,会造成"本地写法不同但类型相同"的体验分裂,因此建议 base tsconfig 统一开启并同步 lint 规则(dot-notation 关、no-dynamic-key 类规则配合)。实践:配合显式接口优先——动态数据先建模接口,索引签名只做兜底。

考察索引签名访问规范的工程落地:点/括号访问的语义区分与跨包一致性,回答应说明选项语义与 Monorepo 统一配置。

#
★★★

2. this 参数类型在链式 API 与 Builder 模式的应用

this 参数类型在链式 API 与 Builder 模式中如何应用?

  • this 参数声明与链式返回类型
  • Builder 中 this 返回实现类型安全的链式调用
  • 与泛型、条件类型的组合

this 参数类型允许方法声明"调用方的 this 上下文":Builder 模式中 step() 方法声明 this: Builder<State> 并返回 this,配合泛型状态参数(class Builder<T> { withX(this: Builder<T & { x: number }>): this })可实现"按调用顺序累积类型状态"的链式 API——调用 withX 后 this 类型自动携带 x 字段,后续方法按当前状态约束参数。价值:其一,类型安全的状态机式链式调用(配置、查询构建、表单编排);其二,方法内部可通过 this 参数获得精确上下文类型(避免宽松 this: any);其三,库作者用 this 类型设计"上下文绑定"API(如事件订阅器内 this 指向订阅对象)。边界:this 参数只影响类型检查不产生运行时参数,与箭头函数(无 this 绑定)组合时需注意。

考察 this 类型在流式 API 的建模能力:this 返回 + 泛型状态累积实现链式类型安全,回答应说明实现模式与边界。

#
★★★

3. DeepReadonly 的递归实现与类型深度限制

DeepReadonly 如何递归实现?类型深度限制如何影响它?

  • 递归映射类型的实现结构
  • 函数/类/Date 等边界的递归终止
  • 实例化深度限制与性能

DeepReadonly 典型实现:type DeepReadonly<T> = T extends (infer E)[] ? readonly DeepReadonly<E>[] : T extends Function | Date | RegExp ? T : { readonly [K in keyof T]: DeepReadonly<T[K]> }——数组递归元素、函数/Date 等"叶类型"终止、对象逐层递归;也可用 [T] extends [object] 判断递归对象,但需注意 string/number 等装箱类型误入。深度限制:TS 实例化递归默认约 50 层(类型实例化深度限制),深层嵌套数据结构(超过 50 层)会报"Type instantiation is excessively deep",复杂条件类型组合递归会更快触顶;性能上深递归 + 大联合放大推断成本。缓解:用条件类型短路(先判原始类型)、拆分为中间类型、或接受"有限深度"(写 3-5 层展开的近似 Deep 类型)用于常规业务深度。

考察递归工具类型的实现与工程边界:叶类型终止、深度上限、性能治理,回答应说明实现结构与限制缓解手段。

#
★★★

4. Omit<T, K> 在 React Props 与表单字段裁剪的应用模式

Omit<T, K> 在 React Props 与表单字段裁剪中有哪些应用模式?

  • Omit 剔除指定键的组合实现
  • React Props 派生(排除内部字段/默认字段)
  • 表单字段子集建模(编辑/新建/预览)

Omit<T, K>(= Pick<T, Exclude<keyof T, K>>)剔除键集合,React 场景的典型模式:其一,组件 Props 派生——type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'value'> 排除与自定义属性冲突的原生属性再扩展;其二,高阶组件透传——包装组件 Props 用 Omit 剔除被消费的字段,其余透传(Omit<BaseProps, 'onChange'> & { onValue?: ... });其三,表单字段裁剪——实体类型 Omit 服务端/内部字段(id、createdAt、_internal)后作为新建/编辑表单模型,编辑态再加 Partial 表达"部分可改",展示态用 Pick 白名单。注意 Omit 对可选属性保留可选性、对联合键有效;复杂裁剪可组合 Pick/Exclude 表达"保留 + 删除 + 覆盖"。

考察 Omit 在 UI 与表单建模的组合应用:原生属性排除、实体字段裁剪、表单阶段模型,回答应展示典型派生模式。

#
★★★

5. TypeScript 编译产物的类型擦除与运行时元数据的边界

TypeScript 编译产物的类型擦除与运行时元数据边界是什么?

  • 类型注解/接口/泛型在产物中的擦除
  • 需要运行时元数据时的替代方案(装饰器、zod、代码生成)
  • 类型与运行时信息不对等的风险

TS 编译时擦除全部纯类型信息:类型注解、接口、类型别名、泛型参数、enum(转成对象但无类型)、类型守卫的谓词等都不进入产物;装饰器(开启时)保留并通过 emitDecoratorMetadata 注入设计时类型字符串(reflect-metadata 或 Symbol.metadata),是"类型信息进入运行时"的少数通道。边界:运行时拿不到"T 是什么"(泛型实例化类型不可见)、接口不存在(instanceof 不可用)、类型谓词不自动生成运行时检查。需求运行时类型信息时的方案:装饰器元数据(DI/ORM 的设计时类型)、zod/valibot schema(运行时校验 + 类型推导)、代码生成(typia 的编译器插件、tsoa/OpenAPI 生成)、显式运行时描述(type field 判别联合)。风险:依赖"类型等价即运行时等价"的假设(如两个同名结构不同字段的对象),或"类型检查通过但运行时数据不匹配",边界入口必须运行时验证。

考察类型系统的运行时边界:擦除是默认、元数据需显式机制,回答应说明擦除范围与获取运行时类型的方案。

#
★★★

6. 类型级 Branded Types + Phantom Types 在测试与 CI 的工程价值

类型级 Branded Types 与 Phantom Types 在测试与 CI 中有什么工程价值?

  • 品牌类型(brand)的模拟名义类型
  • Phantom 类型参数(不参与运行时)
  • 测试与 CI 中的类型隔离与契约验证

Branded Types 用交叉类型附加"名义标签":type UserId = string & { __brand: 'UserId' }——结构与 string 相同但互不赋值,模拟名义类型隔离不同语义的值(UserId 不能赋给 OrderId),避免"同构不同义"的混用 bug;Phantom Types 指不参与运行时、仅类型层存在的类型参数:type Metric<T extends 'km' | 'miles'> = number & { _unit: T }(_unit 是 phantom 字段,擦除后无运行时痕迹),用于单位、货币、物理量等维度约束。测试与 CI 价值:其一,测试夹具构造类型安全——品牌类型强制显式转换(as UserId 或工厂函数),漏标即编译失败;其二,CI 中类型检查即"契约测试"——品牌/phantom 约束把领域不变量编码进类型,误用被 tsc 拦截,配合类型测试(tsd)断言转换函数行为;其三,擦除零开销,不影响产物与运行时。

考察名义类型模拟的工程价值:品牌类型隔离同构值、phantom 参数做维度约束,回答应说明实现与测试/CI 中的契约验证。

#
★★★

7. 类型级 CSS-in-JS(vanilla-extract)

类型级 CSS-in-JS(vanilla-extract)是什么?它如何用类型保障样式契约?

  • vanilla-extract 的"编译期 CSS"模型
  • 样式与主题 token 的类型约束
  • 与设计系统、无运行时样式的配合

vanilla-extract 把"CSS-in-TS"提升为编译期产物:style({ color: theme.colors.primary }) 在构建期生成静态 CSS 文件(零运行时),类型层面:其一,样式对象受 TypedStyle 约束——非法的 CSS 属性/类型错误在编译期报错(属性名、值单位、CSSProperties 类型检查);其二,createTheme/themeContract 定义主题 token 的类型契约,style 引用不存在的 token 编译失败;其三,createVar/sprinkles 生成带类型的 CSS 变量与工具类,配合"设计 token 即类型"(Design Tokens 类型化),实现主题与组件的强类型联动。工程价值:类型成为样式系统的"契约层"——属性合法性、token 引用、主题一致性全部编译期保障,样式产物静态化(性能)且可 tree-shaking;边界:类型检查只覆盖静态写法,动态拼接(CSS 变量字符串)仍需运行时约束。

考察编译期 CSS 的类型保障:属性/token 的类型检查与设计系统联动,回答应说明模型与收益。

#
★★★

8. 类型级 EventEmitter 签名 在测试与 CI 的工程价值

类型级 EventEmitter 签名在测试与 CI 中有什么工程价值?

  • 事件名-载荷映射的类型签名
  • 监听/派发签名的自动约束
  • 测试与 CI 中的事件契约验证

类型级 EventEmitter 用映射类型把事件契约编码:type Events = { 'user:login': { id: string }; 'cart:add': { item: Item } },emit/on 签名用 keyof Events 约束事件名、索引访问约束载荷:emit<K extends keyof Events>(name: K, payload: Events[K])on<K extends keyof Events>(name: K, cb: (p: Events[K]) => void)——事件名拼错、载荷类型不符、监听器参数不匹配全部编译期报错。测试与 CI 价值:其一,事件契约"类型即文档",新事件在 Events 映射中声明后所有使用点自动受约束;其二,测试中 mock 事件发射时载荷类型强制正确,减少"测试写错事件"的无效用例;其三,CI 的类型检查把事件 API 的破坏性变更(改名、改载荷)暴露为编译错误,配合类型测试(expectTypeOf 断言 emit 签名)固化契约;其四,配合判别联合与穷尽检查可在监听器内安全收窄。

考察事件总线的类型化契约:映射类型约束名-载荷联动,回答应说明签名设计与测试/CI 中的验证价值。

#
★★★

9. 递归类型边界与函数类型在工具类型中的处理

递归类型边界与函数类型在工具类型中如何处理?

  • 递归终止与函数类型的叶节点处理
  • 函数类型被误递归展开的问题
  • 常见 Deep 系列对函数的处理策略

递归工具类型(DeepReadonly/DeepPartial/DeepRequired 等)需要处理"何时停止递归",函数类型是典型叶节点:DeepReadonly<T> = T extends Function ? T : { readonly [P in keyof T]: DeepReadonly<T[P]> }——函数不能被映射展开(映射会破坏签名且语义错误),Date、RegExp、Map、Set、Promise 等特殊对象也应终止或按语义处理(如 Promise 解包、Map 键值递归)。若不加边界,DeepReadonly<() => void> 会被 { [K in keyof T] } 展开为包含 call/apply 等方法的畸形类型,甚至无限递归。处理策略:先判原始类型(string/number/boolean/symbol/bigint/null/undefined)与 Function 终止,再处理数组/元组(递归元素),对象分支递归属性;对类实例按"不递归"或"只递归公开字段"取舍;边界类型用 [T] extends [X] 防止联合分布导致误判。工程上把"叶类型集合"提取为共享条件,保证各 Deep 系列行为一致。

考察递归工具类型的边界设计:函数与特殊对象的终止是正确性关键,回答应说明叶类型集合与处理策略。

#
★★★

10. as const satisfies 在路由表/配置对象的联合工程价值(保留字面量 + 校验)

as const satisfies 在路由表/配置对象中如何实现"保留字面量 + 校验"的联合工程价值?

  • as const 保留字面量与 satisfies 校验形状的组合
  • 路由表/配置表的类型派生
  • 与枚举、联合手写对比的收益

as const satisfies 组合:satisfies 校验表达式形状(满足目标类型),as const 保留字面量窄类型——const routes = [...] as const satisfies readonly Route[]:若某路由缺字段/类型不符则编译报错,同时 routes 的字面量类型完整保留,后续用 (typeof routes)[number]['path'] 派生路径联合、用 keyof 派生配置键。相比手写联合(易漂移)与 enum(运行时对象/无校验),该模式"单一数据源 + 编译期校验 + 精确派生":路由表新增路由自动进入路径联合、配置对象新增键自动进入键联合,i18n 表、权限表、表单字段表同理。工程价值:消除"类型声明与数据表不一致"的漂移,改表即改类型;注意 as const 与 satisfies 的顺序(as const 先、satisfies 后校验整体),以及 readonly 化对可变操作的影响。

考察"数据即类型"的现代模式:as const 保留字面量、satisfies 校验形状,回答应说明组合语义与派生收益。

#
★★★

11. Brand Type(Nominal Type)的类型模拟与类型安全的工程价值

Brand Type(Nominal Type)如何模拟名义类型?有哪些类型安全的工程价值?

  • 品牌交叉类型与唯一标签
  • 构造函数/守卫的强制转换
  • 防混用、防单位错误、防 ID 错传

Brand Type 用结构交叉 + 品牌标签模拟名义类型:type UserId = string & { readonly __brand: unique symbol }(unique symbol 防伪造标签),或 string & { __brand: 'UserId' };转换必须经过工厂函数/守卫:const toUserId = (s: string): UserId => s as UserId,业务代码只能通过工厂获得品牌值。工程价值:其一,防 ID 混用——UserId、OrderId、TeamId 结构相同但互不赋值,接口传参顺序错误编译期拦截;其二,防单位/货币错误——type Meters = number & { _unit: 'm' },米与公里互不混算;其三,防"普通 string 冒充标识"——外部输入必须显式转换,转换点即校验点;其四,配合守卫与 schema 在边界验证后品牌化,内部类型安全。代价:需显式转换(运行时零开销)、跨库传递品牌需重新构造、滥用增加样板;适合领域模型与高风险参数。

考察名义类型的类型层模拟:品牌标签 + 显式转换点,回答应说明实现、应用场景与成本。

#
★★★

12. TypeScript Playground 在类型诊断与教学协作的应用

TypeScript Playground 在类型诊断与教学协作中如何应用?

  • Playground 的实时编译与类型查看
  • TS 版本切换与配置模拟
  • 分享与教学(错误定位、类型可视化)

TypeScript Playground(tsplay.dev)是浏览器内的 TS 实验环境:实时类型检查与编译预览、悬停查看推断类型、右侧显示 JS 产物与 .d.ts、支持切换 TS 版本(含 nightly)与 compilerOptions 模拟、AST 视图与类型浏览器(Type Explorer)可视化结构。应用场景:类型诊断——复现"奇怪的推断/报错",切换版本对比行为差异,生成最小复现链接(带版本与配置)提交 issue 或求助;教学协作——演示工具类型实现(Partial/DeepReadonly)、逐步展示推断过程、对比写法差异(satisfies vs as),分享链接让协作者直接看到类型结果;配置验证——快速验证 tsconfig 选项组合(strict 家族、moduleResolution)对代码的影响。工程建议:复杂类型问题"最小化到 Playground 复现"是高效沟通的第一步。

考察 Playground 作为诊断与教学工具的价值:版本/配置模拟、类型可视化、链接分享,回答应说明典型用法。

#
★★★

13. Type-Level Unit(在类型系统表达单位与单位运算)

Type-Level Unit 如何在类型系统表达单位与单位运算?

  • 单位类型的 phantom 建模
  • 单位运算(加减乘除)的类型推导
  • 维度分析(Dimensional Analysis)的应用

Type-Level Unit 用 phantom 类型参数表达物理/业务单位:type Unit<M, KG, S> = number & { _unit: [M, KG, S] }(指数元组表示量纲),type Meters = Unit<1, 0, 0>type Seconds = Unit<0, 0, 1>;运算通过类型函数推导量纲——加法要求量纲一致(参数约束 Unit<A, B, C> 且返回同量纲)、乘法指数相加(Multiply<Unit<A,B>, Unit<C,D>> = Unit<A+C, B+D>)、除法指数相减,实现"速度 = 距离 / 时间"这类运算的类型级验证。工程应用:物理引擎、金融单位(货币/利率)、数据单位(字节/KB 换算)的维度分析——混用单位编译期报错、换算公式类型化;实现上用元组指数(长度可变)与条件类型做加法(递归或数组长度技巧)。边界:量纲计算复杂度随指数维度增长,复杂公式需拆中间类型;运行时仍是 number,需配合工厂函数构造。

考察单位系统的类型级实现:phantom 量纲 + 类型运算推导,回答应说明建模与运算推导方式。

#
★★★

14. 严格模式家族(strictNullChecks、strictFunctionTypes、noImplicitAny、exactOptionalPropertyTypes)的逐项开启策略

严格模式家族(strictNullChecks、strictFunctionTypes、noImplicitAny、exactOptionalPropertyTypes)如何逐项开启?

  • 各选项的语义与报错范围
  • 开启顺序与迁移成本评估
  • 与 lint、代码改造的配合

strict 家族逐项开启的推荐顺序按"迁移成本从低到高":其一,noImplicitAny——参数/变量隐式 any 报错,补显式类型即可,成本低收益大(先行);其二,strictNullChecks——null/undefined 纳入类型,报错面最大(涉及所有可空值),需配合空值守卫与 ??/! 改造,建议分区推进;其三,strictFunctionTypes——函数参数逆变收紧,回调/事件处理器赋值场景报错,量中等;其四,strictPropertyInitialization(配合 strictNullChecks)——类属性初始化检查;最后 exactOptionalPropertyTypes——可选属性语义收紧,库与 API 契约场景报错,业务影响面小但语义改动深。策略:base 配置直接开 strict(含前三项),遗留代码用目录级覆盖逐步收敛;每个选项开启后跑全量错误清单,按模块分批修复,配合 lint(no-unnecessary-type-assertion 等)防止"用断言掩盖";CI 把关新代码,存量错误计数递减归零。

考察严格模式的增量迁移:按成本排序逐项开启 + 分区推进,回答应说明各选项报错面与迁移手法。

#
★★★

15. Equal<X, Y>/IsAny 等类型测试 helpers(来自 expect-type 等库)

Equal<X, Y>、IsAny 等类型测试 helpers(如 expect-type)如何工作?

  • Equal 对"双向可赋值"的判断实现
  • IsAny 对 any 的识别
  • 在类型测试与工具库中的角色

判断两个类型"完全相等"比可赋值更严格,expect-type 的 Equal<X, Y> 实现用函数参数逆变技巧:type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false——利用"泛型函数的类型等价性"精确判断,可区分 anyunknown(可赋值相等但 Equal 为 false)等边缘;IsAny 利用 0 extends (1 & T)any 的分布特性:type IsAny<T> = 0 extends (1 & T) ? true : false(any 与 1 相交后仍可被 0 扩展)。应用:expectTypeOf 内部基于 Equal 族实现 toEqualTypeOf(严格相等)、toMatchTypeOf(可赋值);自定义工具类型测试断言相等关系、检测意外 any/never;注意普通 X extends Y ? true : false 判断不出"相等"(单向可赋值),Equal 必须双向精确判定。

考察类型相等判定的实现原理:逆变技巧 + any 识别,回答应说明 Equal/IsAny 的实现与测试角色。

#
★★★

16. 类型编程实战与运行时方案(zod、io-ts、valibot)

类型编程实战如何与 zod、io-ts、valibot 等运行时方案协作?

  • schema 的 infer 类型与校验一体化
  • 三种库的定位差异
  • 类型编程与运行时校验的分工

zod/io-ts/valibot 的共性:"schema 定义运行时校验 + infer 导出静态类型",一份定义同时产出校验器与类型;差异:zod 生态最大、API 直观(z.object/z.string 链式),运行时体积较大;io-ts 更函数式/严谨(基于 fp-ts、编解码分离,支持编解码往返);valibot 追求极小体积(按需模块化、tree-shaking 友好),API 与 zod 相似。类型编程的协作模式:其一,复杂类型(判别联合、递归、brand)先用类型编程定义,schema 用其形态(或反过来 schema 先行 infer 类型);其二,schema 的 refine/superRefine 做"运行时无法用类型表达"的校验(跨字段、异步),类型层保证形状;其三,brand 类型经 schema 的 brand 方法转换,边界校验后进入类型安全域;其四,tRPC/表单等"端到端"场景用 schema 同时驱动客户端类型与服务端校验。选型按体积、生态、函数式偏好决定。

考察 schema 库与类型编程的协作:一份定义双重产出、边界校验入域,回答应说明三库差异与分工模式。

#
★★★

17. 类型挑战(type-challenges)高频题目(Pick、Readonly、DeepReadonly)

type-challenges 高频题目(Pick、Readonly、DeepReadonly)的实现要点是什么?

  • 手写 Pick/Readonly 的映射基础
  • DeepReadonly 的递归与边界
  • 手写实现与内置类型的差异

type-challenges 系列训练类型编程原语:手写 Pick 用 { [P in K]: T[P] }(K extends keyof T 约束键)、Readonly 用 { readonly [P in keyof T]: T[P] }、DeepReadonly 递归:T extends Function ? T : { readonly [P in keyof T]: DeepReadonly<T[P]> }(数组分支可用 T extends readonly any[] 单独处理,或依赖对象映射的索引处理)。要点:其一,键约束(extends keyof)保证索引访问安全;其二,递归必须有叶类型终止(Function/原始类型);其三,与内置类型的差异——内置 Pick 允许 K 含非 T 的键?(实际内置为 Pick<T, K extends keyof T ? K : never> 或宽松约束,行为以测试为准),内置 Readonly 是浅层,手写实现边界(数组/联合)常暴露对分布条件类型与元组的理解;训练价值在于把"会用"提升为"能写",理解工具类型的内部契约。

考察类型编程基础题的实现要点:映射、键约束、递归终止,回答应说明每题实现与常见陷阱。

#
★★★

18. type TupleToObject 与元组转对象在常量数组键的工程应用

type TupleToObject 如何实现?元组转对象在常量数组键场景有什么工程应用?

  • 元组转对象(值作键)的映射实现
  • as const 数组与对象键的联动
  • 配置/字段白名单的键推导

TupleToObject 把元组的值映射为对象的键:type TupleToObject<T extends readonly (string | number | symbol)[]> = { [P in T[number]]: P }——T[number] 取元组元素联合,映射为"值即键、键值相同"的对象。工程应用:其一,白名单配置——const FIELDS = ['name', 'email'] as consttype FieldKeys = keyof TupleToObject<typeof FIELDS> 推导允许字段联合,表单/表格列配置"数组声明即键集合";其二,常量数组驱动枚举——错误码、状态列表用数组(可遍历)同时推导联合类型与映射对象(code → 描述);其三,与 keyof、as const satisfies 组合,保证数组元素与类型声明同步。注意元组须为 readonly 且元素为合法键类型;空元组推导为空对象。

考察元组到键集合的类型转换:T[number] + 映射,回答应说明实现与"数组即键源"的工程模式。

#
★★★

19. type First<T extends any[]> 等元组访问类型在数组工具函数的工程应用

type First<T extends any[]> 等元组访问类型在数组工具函数中如何应用?

  • 元组首元素/元素访问的索引与 infer 实现
  • 空元组的边界处理
  • 数组工具函数(首元素、去头、查长)的类型化

First 的两种实现:索引访问 T[0](空元组为 undefined)或 infer T extends [infer F, ...any[]] ? F : never(空元组为 never);其他元组访问:Last[...any[], infer L])、PopT extends [...infer R, any] ? R : never)、LengthT['length'])等。工程应用:其一,数组工具函数(首元素、末元素、去尾、切段)返回类型随输入元组精确推导,避免 any 化;其二,事件/参数元组解析——从元组参数取某位置的类型;其三,变长参数(rest 元组)的变换([...Head, New] 追加);边界:空元组的语义(never vs undefined)需按场景约定;readonly 元组需加 readonly 约束才能匹配。这些访问类型是"元组即数据结构"类型编程的基础零件。

考察元组访问类型族:索引/infer 实现与空元组边界,回答应说明实现与数组工具函数应用。

#
★★★

20. type Length<T extends readonly any[]> 在元组长度推导的工程价值

type Length<T extends readonly any[]> 在元组长度推导上有什么工程价值?

  • 元组 length 属性作为字面量数字类型
  • 数组与元组的 length 差异
  • 长度参与类型运算(维度、步长)的应用

元组的 length 属性类型是字面量数字:Length<['a','b','c']> 推导为 3(T['length']),而普通数组的 length 是 number;约束 T extends readonly any[] 保证匹配元组(含 readonly)。工程价值:其一,固定参数/维度校验——矩阵、向量、坐标、RGB 数组的长度参与类型检查(传 4 长度给 3 维 API 编译报错);其二,长度做类型运算——利用元组长度模拟数字运算(Tuple<number> 生成指定长度元组,长度即"数字"),实现类型级加法/减法/比较;其三,步进/分页的固定长度建模(如每页 N 条的元组类型)。注意:as const 数组才有字面量长度(普通数组 length: number);length 推导依赖元组声明(含 readonly)。

考察元组长度的字面量语义:T['length'] 的推导与"长度即数字"的类型编程价值,回答应说明约束与运算应用。

#
★★★

21. type LookUp<U, T extends U['type']> 在判别联合筛选的工程应用

type LookUp<U, T> 如何在判别联合中筛选成员?有什么工程应用?

  • 按判别字段筛选联合成员的实现
  • 分布条件类型的筛选机制
  • 事件/消息/状态分支的按类型取用

LookUp 用分布条件类型筛选:type LookUp<U, T> = U extends { type: T } ? U : never——裸类型参数 U 触发分布式展开,逐个成员判断 type 是否匹配,命中的成员保留(never 剔除的成员在联合中自动消失)。工程应用:其一,消息/事件处理——LookUp<EventUnion, 'click'> 直接取点击事件类型(含其专属载荷),避免手工声明中间类型;其二,状态机分支按 status 取状态类型,配合 switch 收窄;其三,构建"按类型筛选"的工具组合(过滤 + 提取 + 转换)。要点:判别字段须为字面量(type: T 匹配字面量);未命中返回 never(空联合);与 Extract 的区别——Extract 按"整体可赋值"筛选,LookUp 按"判别字段值"筛选(在判别联合中更精准)。

考察判别联合的按字段筛选:分布条件类型 + 字面量匹配,回答应说明实现与消息/状态场景应用。

#
★★★

22. Template Literal Types 在 i18n key 与 CSS-in-JS 的工程价值

Template Literal Types 在 i18n key 与 CSS-in-JS 中有哪些工程价值?

  • 模板字面量约束 key 格式与组合
  • i18n key 的自动补全与校验
  • CSS-in-JS 类名/变量的类型生成

i18n 场景:用模板字面量类型把 key 约束为命名规范——type TKey = \${Module}.${Action}` 或从 as const 的翻译表推导 key 联合(keyof typeof messages),业务代码写 key 时自动补全、拼写错误编译期拦截;动态拼接(t(`${module}.${name}`))在变量为字面量时推导出精确 key,配合框架 t 函数签名校验 key 存在。CSS-in-JS:类名生成——`${Base}-${Variant}` 由变体联合推导类名联合,样式系统类型化(vanilla-extract、style objects);CSS 变量——`--${Token}`` 约束变量名与 token 表联动。工程价值:把"字符串约定"升级为编译期契约,key/类名变更影响全链路暴露,与 as const、satisfies 组合保持单一数据源。

考察模板字面量在字符串约定契约化的应用:key 约束、补全、联动,回答应说明 i18n 与 CSS-in-JS 的典型模式。

#
★★★

23. TypeScript Compiler API 的基本使用,ts.createProgram、TypeChecker 与 AST 遍历(visitor 模式)在代码生成中的应用

TypeScript Compiler API(ts.createProgram、TypeChecker 与 AST 遍历)如何在代码生成中应用?

  • createProgram 建立程序与语法/语义信息
  • TypeChecker 的类型查询(getTypeAtLocation 等)
  • AST 遍历与代码生成/转换

Compiler API 以 ts.createProgram 为入口:传入文件列表与 compilerOptions 建立 Program,内部含 SourceFile(AST)与 TypeChecker(类型查询)、TypeSystem 等;AST 遍历用 ts.forEachChild/visitNode(visitor 模式)或 ts.factory 构造新节点,配合 ts.transpileModule/Printer 输出代码。代码生成应用:其一,codemod——遍历 AST 按模式改写代码(重命名、API 迁移、import 整理),用 ts.transform 的 transformer(before/after)挂在 tsc 或独立脚本;其二,类型驱动生成——用 TypeChecker 查询类型信息(getTypeAtLocation、getSymbolAtLocation、getTypeOfSymbolAtDeclaration)生成文档/协议/类型对照文件(如 tsoa、typia 模式);其三,诊断工具——用 getSemanticDiagnostics 自定义检查。要点:语义查询需先 getTypeChecker;AST 修改用 factory 重建保持位置信息(或独立工具用 printer 输出新文件)。

考察 Compiler API 的入口与模式:Program/TypeChecker/AST 遍历的分工,回答应说明代码生成与迁移工具的实现路径。

#
★★★

24. typescript-eslint 类型感知规则(parserOptions.project)的实现原理与 no-floating-promises/no-misused-promises 等规则的类型信息获取

typescript-eslint 类型感知规则(parserOptions.project)的实现原理是什么?no-floating-promises 等规则如何获取类型信息?

  • parser 与 type-aware linting 的协作(parserOptions.project)
  • TypeChecker 在规则中的使用(services.program)
  • 类型感知规则的性能与配置代价

typescript-eslint 的 type-aware linting 原理:parser(@typescript-eslint/parser)在 parse 时若配置 parserOptions.project(指向 tsconfig),会调用 TypeScript Compiler API 建立 Program(完整类型系统),并把 services 注入 ESLint 的上下文(context.sourceCode.parserServices.program/typeChecker);规则实现中通过 parserServices.program.getTypeChecker() 获取类型,结合 AST 判断——no-floating-promises 检查"未处理的 Promise 表达式"(getTypeAtLocation 判断类型是否 Promise 且无 await/链式调用)、no-misused-promises 检查"Promise 被当作条件/返回值误用"、no-unnecessary-type-assertion 检查"多余断言"(类型相等性)。代价:建立 Program 使 lint 显著变慢,需缓存与增量(projectService 模式缓解);配置需 parserOptions.project 与 tsconfigRootDir 正确;规则使用类型信息标注为"requires type information"(文档标注 + 性能提示)。

考察 type-aware linting 的架构:parser 借 Compiler API 注入 TypeChecker,规则按需查询类型,回答应说明机制与性能治理。

#
★★★

25. ts-morph 的 Project/SourceFile/API 在代码迁移工具(codemod)中的工程价值与 Compiler API 的封装差异

ts-morph 的 Project/SourceFile API 在 codemod 中的工程价值是什么?它与 Compiler API 的封装差异如何?

  • ts-morph 的高层对象模型(Project/SourceFile/Class/Function)
  • 链式查询与自动重写(format/保存)
  • 与裸 Compiler API 的封装差异

ts-morph 在 Compiler API 之上提供面向对象的封装:Project(管理文件集合与配置,可读 tsconfig)、SourceFile(文件级操作:getClasses/getFunctions/findDescendants 等查询 API、addImport/removeClass 等修改 API、formatText/保存),修改自动处理结构(如删除节点后 AST 一致性)并用 prettier 风格重排。codemod 价值:其一,可读性——声明式查询与修改(sourceFile.getClasses().filter(c => c.getName() === 'X').forEach(c => c.rename('Y'))),比裸 visitor 回调直观;其二,安全——结构化编辑(addNode/removeNode)配合自动保存,避免文本级替换破坏格式;其三,迁移工作流——读入源码目录、批量转换、统一格式化,支持复杂重构(API 改名、import 整理、类转换)。与 Compiler API 差异:ts-morph 更"程序员友好"但抽象层带来性能开销与灵活性限制(自定义 transformer 等高级场景仍需裸 API);选择:常规 codemod 用 ts-morph,深度编译参与(自定义 transformer、类型级生成)用裸 API。

考察 codemod 工具的抽象层次:ts-morph 的声明式对象模型 vs 裸 Compiler API 的底层灵活性,回答应说明差异与选型。

#
★★★

26. API Extractor 在库 API 变更治理中的应用,api-report、api-documenter 与 breaking change 检测

API Extractor 在库 API 变更治理中如何应用?api-report、api-documenter 与 breaking change 检测如何工作?

  • API Extractor 的 .d.ts 分析管线
  • api-report 的 API 快照与 diff 审查
  • api-documenter 生成文档与版本治理

API Extractor(Microsoft)消费库的 .d.ts 产物,生成规范化的 API 模型(api.json),支撑三件事:其一,api-report——把公开 API 汇总为 api.md 快照,提交 PR 时 diff 即"API 变更清单"(新增/修改/删除导出),配合 CI 审查(api-extractor run 校验快照一致性,api-extractor rollup 可选打包声明);其二,breaking change 检测——对比版本间 api.json/api.md,识别删除导出、签名变化等破坏性变更,人工确认后打对应版本(minor/major),实现"类型契约的版本管理";其三,api-documenter——从 api.json 生成 Markdown API 文档(页面组织、签名渲染、@remarks 注释),文档与代码同源。工程实践:库 CI 中 api-extractor 作为必过检查,PR 模板要求附 API diff 说明,破坏性变更走审批流程;与 api-extractor 的 enum/namespace 模型结合 isolatedDeclarations 产出更稳。

考察库 API 治理链路:分析-快照-文档-变更检测,回答应说明三工具的分工与 CI 流程。

#
★★★

27. 类型编程在 zod、tRPC、Elysia 等现代库中的类型驱动设计应用

类型编程在 zod、tRPC、Elysia 等现代库中的类型驱动设计如何应用?

  • schema 推断(infer)的端到端类型流
  • tRPC 的过程类型(procedure 类型)推导
  • Elysia 的泛型路由与上下文类型

三个库展示"类型驱动设计"(Type-Driven Development):zod 用 schema 推断——z.object({...}).infer 导出输入/输出类型,校验与类型单源;tRPC 把"过程"(procedure)类型化——createTRPCRouter 的输入/输出/中间件类型在定义处推导,客户端 trpc.user.get 的调用签名(参数/返回值)从服务端类型自动同步,实现类型安全的 RPC 链路(全程无手写类型);Elysia 用泛型路由与字面量路径推导——路由字符串模板字面量类型推导参数对象、上下文(headers/query/body)类型经中间件链式累积,处理器参数类型自动对齐。共同技术:模板字面量类型、infer/条件类型、泛型累积(对象逐层 &)、函数签名提取;工程价值:契约单一来源,前后端类型同步,编译期捕获协议漂移;代价:类型复杂度高、报错信息深,需要类型编程能力维护。

考察类型驱动框架的设计范式:schema 单源、过程类型推导、泛型累积,回答应说明各库机制与共同的技术基础。

#
★★★

28. 跨站 iframe 在进程隔离下的内存与调度成本

跨站 iframe 在进程隔离下有什么内存与调度成本?

  • 跨站 iframe 的进程/线程隔离机制
  • 内存与渲染调度的开销
  • 大量 iframe 场景的性能治理

现代浏览器对跨站 iframe 采用站点隔离(site isolation):每个跨站 iframe 放入独立渲染进程(Site Isolation 机制),隔离收益是安全(沙箱、内存破坏防护)与稳定性(崩溃隔离),成本:其一,内存——每个进程拥有独立堆、渲染器、GPU 通道与合成层,iframe 数量多时内存占用线性放大(V8 堆、图层树、纹理);其二,调度——进程间通过 IPC 通信(主帧与子帧的输入/合成/光栅协调),频繁跨进程消息增加延迟与 CPU 开销,动画/滚动场景尤其明显;其三,启动成本——新进程创建、共享内存初始化、缓存未命中。工程治理:减少不必要的跨站 iframe(优先同站嵌入/组件化)、懒加载与按需挂载 iframe、控制 iframe 数量与生命周期(卸载即销毁)、用 postMessage 合并通信减少 IPC 频率、监控内存峰值(Performance.measureUserAgentSpecificMemory)。

考察浏览器进程模型的成本面:站点隔离的收益与内存/调度代价,回答应说明机制与治理手段。

#
★★★

29. ts-expect-error vs @ts-ignore 的语义边界与使用规范

ts-expect-error 与 @ts-ignore 的语义边界与使用规范是什么?

  • 两指令的触发条件差异
  • @ts-expect-error 的自检特性
  • 团队规范与 lint 限制

二者都抑制下一行错误,边界差异:@ts-expect-error 期望"下一行有错误",若下一行没有错误则自身报"未使用的指令"(unused '@ts-expect-error' directive)——错误被修复时自动暴露,是"可自检的抑制";@ts-ignore 无条件忽略下一行错误,错误消失后静默无感,容易掩盖回归。使用规范:优先 @ts-expect-error(新代码强制),用于第三方类型缺失、已知待修复的临时绕过,并要求注释理由(// @ts-expect-error - lib types incomplete, TODO);@ts-ignore 仅用于无法满足 expect-error 语义的存量(或宏/特殊场景);lint 配置 ban-ts-comment 允许 expect-error、禁止 ignore/nocheck(或降级 warn),配合 ESLint 的 reportUnusedDisableDirectives 在 CI 中清除"已无用的抑制",存量抑制数量纳入技术债追踪逐步归零。

考察抑制指令的语义差异与治理:expect-error 自检特性是核心,回答应说明边界、规范与 lint 治理。

#
★★★

30. 类型层级解析(Type Hierarchy)的可视化与推断行为

类型层级解析(Type Hierarchy)如何可视化?对理解推断行为有什么帮助?

  • 类型层级的基本结构(any/unknown/never/联合)
  • IDE 的类型层级视图(VSCode 的 Go to Type Hierarchy)
  • 层级对可赋值与收窄的影响

类型层级描述类型的子类型关系:底层是 never(任何类型的子类型),顶层是 unknown(任何类型的超类型),any 同时兼容双向(特殊),具体类型按结构化规则分层,联合类型由成员决定层级区间。可视化工具:VSCode 的 "Go to Type Hierarchy"(F12 家族)可查看接口/类的层级树(父类型、子类型、实现);Playground 的 Type Explorer 与 hover 显示推断结果;ts-graph 等第三方工具绘制类型依赖图。理解层级对推断行为的帮助:其一,可赋值性方向——值类型(下层)可赋给宽类型(上层),反之不行,报错时可推断"目标在层级中过高/过低";其二,收窄——联合收窄是"把当前值向下移动到更精确的层级",never 分支表示层级为空;其三,any 的特殊性——any 在层级外(双向兼容),解释"为什么 any 能绕过一切检查"。排查类型错误时先画层级(谁是谁的子类型)能快速定位方向性问题。

考察类型层级的可视化与推断关联:层级方向决定可赋值,回答应说明工具与诊断价值。

#
★★★

31. 复杂条件类型导致编译变慢或报错难读时的治理(拆中间类型、命名)

复杂条件类型导致编译变慢或报错难读时如何治理?

  • 类型实例化成本与"类型实例化过深"报错
  • 拆中间类型与命名类型别名
  • 报错信息的可读性提升

复杂条件类型(多层递归 + 大联合 + 深层映射)的治理分三层:其一,拆解——把巨型条件类型拆为具名中间类型(如把 ParsePath<T> 的复杂表达式拆成 Head、Tail、Join 等小步骤),每个步骤单一职责、可单独测试;其二,替换——用映射类型/索引访问替代链式条件(映射天然并行处理键)、用 [T] extends [X] 阻止意外分布、缓存部分结果(type Cache<X> = ... 先行展开);其三,降复杂——避免"全量推导"设计(改用有限深度、白名单联合、泛型参数化而非全枚举)。性能治理:限制联合规模与递归深度、把重类型从热路径(渲染组件 Props)移出、用 lint/性能分析(tsc --extendedDiagnostics 观察 Instance Count/Type Count)定位热点。报错可读性:命名中间类型让报错链显示语义化名称、避免匿名深层嵌套;用类型测试固化行为,重构时行为不变。

考察复杂类型工程化:拆解、缓存、降复杂与诊断手段,回答应说明报错可读性与性能的可度量治理。

#
★★★

32. assertNever 与 exhaustiveness check 在联合类型分支穷尽的工程价值

assertNever 与 exhaustiveness check 在联合类型分支穷尽上有什么工程价值?

  • assertNever 函数的签名与使用
  • default 分支的穷尽断言
  • 判别联合新增分支的编译期保护

assertNever 定义为 function assertNever(x: never): never { throw new Error('unexpected: ' + x) }:在 switch 处理完所有已知分支后,default 分支把剩余值传入——若联合类型存在未处理分支,x 的类型不是 never,调用处编译报错(参数类型不匹配);若已穷尽,x 收窄为 never,正常运行并在运行时兜底抛错。工程价值:其一,"漏分支"编译期暴露——状态机、消息处理器、表单阶段新增分支时,所有消费点立即报错强制补全;其二,运行时防御——assertNever 兜底抛错防止静默进入非法状态;其三,与 never 收窄配合——穷尽检查让"加分支"成为"全链路编译期任务"。实践:default 分支必须调用 assertNever 或赋值断言(const _: never = value),lint(no-fallthrough 等)保证每个 switch 处理;注意 union 中含 any/unknown 时穷尽检查失效。

考察穷尽性检查的工程模式:never 断言 + 运行时兜底,回答应说明实现与"新增分支自动告警"的价值。

#
★★★

33. Type-level recursion 与 depth limit([T] extends [never] 终止)

类型级递归如何用 [T] extends [never] 终止?depth limit 如何影响?

  • 递归终止条件的写法与分布抑制
  • [T] 包装阻止联合分布的原因
  • 深度限制与递归设计的权衡

条件类型对裸类型参数做分布式展开,递归终止条件若写 T extends never 会把"联合中的 never"逐个剔除而非整体判断,导致终止逻辑失效;[T] extends [never] 把 T 包装进元组,阻止分布(元组不是裸类型参数),对"整个 T"做判断——用于"递归到空联合/never 即停止"的终止条件,如 type Rec<T> = [T] extends [never] ? Done : ...。depth limit:TS 类型实例化递归默认约 50 层,超限报"Type instantiation is excessively deep";深度设计需考虑:其一,递归层数与输入规模(大联合成员数放大展开组合);其二,用"扁平迭代"(映射、索引访问)替代深递归;其三,必要时拆解为多步类型(每步有限深度)。工程上"有限深度 + 明确终止"优先于"无限通用"。

考察递归终止的类型级技巧:元组包装抑制分布、never 终止判断、深度限制,回答应说明机制与设计权衡。

#
★★★

34. Template Literal Types 与运行时验证(zod/valibot)

Template Literal Types 与运行时验证(zod/valibot)如何协作?

  • 模板字面量的编译期模式约束
  • 运行时字符串验证的边界
  • "类型约束 + schema 校验"的双层契约

模板字面量类型在编译期约束字符串模式(${string}-${string}、字面量组合),但运行时拿到的数据(API、用户输入)可能是任意字符串,类型检查无法阻止"运行时出现不符合模式的值";zod/valibot 提供运行时模式校验:z.string().regex()、z.enum()、z.literal() 等,校验通过后类型收窄到对应字面量/模式。协作模式:其一,双层契约——类型层用模板字面量表达"合法形态",schema 层用正则/枚举实现同一规则,保证"类型允许的"与"运行时接受的"一致;其二,schema 推导——复杂模式先写 schema(z.custom 或 regex),infer 类型自动携带;模板字面量能表达的(字面量联合、前缀模式)用类型,不能表达的(复杂正则、长度、语义约束)交给 schema;其三,边界转换——入口校验(schema.parse)成功后断言为模板字面量类型,进入类型安全域。注意双写漂移:用"schema 先行 + infer"或类型测试对齐二者。

考察编译期模式与运行时校验的分工:模板字面量管编译期、schema 管运行时,回答应说明双层契约与同步策略。

#
★★

35. 类型级 SQL Builder 在测试与 CI 的工程价值

类型级 SQL Builder 在测试与 CI 中有什么工程价值?

  • SQL 构建链的类型安全(表/列/条件)
  • 类型级校验替代部分集成测试
  • CI 中类型检查的契约作用

类型级 SQL Builder(如 Kysely、TypeORM QueryBuilder 的类型化版本)把 SQL 构建链编码为类型:db.selectFrom('users').where('age', '>', 18)——表名必须是 schema 类型中的表、列名必须是该表列、操作符与值类型匹配(age 列不能与字符串比较),整个链式调用的"合法状态"由类型状态机表达,拼错表/列/类型编译期报错。测试与 CI 价值:其一,契约先行——schema 类型(Table 接口)即"数据库契约",应用层查询从编译期受约束,运行时 SQL 错误大幅减少(尤其重构表结构时所有查询自动暴露);其二,替代部分测试——类型检查覆盖"表名列名合法性、类型匹配"这类本需集成测试的层面(但数据结果与性能仍需真库测试);其三,CI 的 tsc 检查把"查询与 schema 漂移"变成编译失败,配合 schema 变更(迁移)流程,迁移 + 类型同步落地。注意:动态 SQL(拼接、字符串建表名)会绕开类型保护,需在边界显式处理。

考察类型级 SQL 的契约价值:链式类型状态机把查询合法性编译期化,回答应说明收益与动态 SQL 边界。

#
★★

36. type Push<T extends any[], U> 在元组追加元素的类型工具实现

type Push<T extends any[], U> 如何实现?元组追加元素有什么应用?

  • rest 元组拼接([...T, U])的实现
  • 元组作为"可变列表"的类型操作
  • 在参数累积、状态栈建模中的应用

Push 用 rest 元组展开拼接:type Push<T extends any[], U> = [...T, U]——返回新元组,元素类型 U 追加末尾(Pop 为 T extends [...infer R, any] ? R : never、Unshift 为 [U, ...T]);约束 T extends any[] 接受任意元组/数组。应用:其一,参数累积——builder 模式中逐次收集参数为元组类型,最终签名从累积元组推导(BuildArgs<Args> = (...args: Args) => void);其二,状态栈建模——把"操作历史"建模为元组类型(Push 记录状态演进),配合判别联合实现类型级流程;其三,类型工具组合——Concat([...A, ...B])、Tail、Last 等基于同一 rest 机制。边界:rest 元素(any[])与 readonly 元组需适配(readonly 用 readonly 前缀约束),无限递归/超大元组影响推断。

考察元组操作的类型实现:rest 展开拼接与组合工具,回答应说明实现与参数累积等应用。

#
★★

37. type Concat<T extends any[], U extends any[]> 在元组拼接的类型工具

type Concat<T extends any[], U extends any[]> 如何实现?有什么应用?

  • rest 双展开拼接([...T, ...U])的实现
  • 元组拼接与数组泛型的差异
  • 组合场景(参数合并、链式累积)

Concat 用双 rest 展开:type Concat<T extends any[], U extends any[]> = [...T, ...U]——按序拼接两个元组,返回新元组类型;数组输入时结果退化为同元素联合的数组。应用:其一,参数合并——两个函数签名参数元组拼接成新签名(组合 API 包装、装饰器参数透传);其二,链式累积——builder/中间件链中把各段收集的参数元组拼接,最终调用签名精确推导;其三,与其他元组工具(Push/Unshift/Split)组合实现复杂元组变换(如按索引插入)。边界:readonly 元组需要 readonly any[] 约束(或声明展开兼容),超大元组拼接影响推断性能;与 JS 的数组 concat 不同,类型层 Concat 不产生运行时值,仅类型推导。

考察元组拼接的实现与组合应用:双 rest 展开,回答应说明实现、readonly 边界与签名推导场景。

#
★★

38. type MyAwaited<T extends Promise> 在 Promise 解包类型的工程应用

type MyAwaited<T extends Promise> 如何实现?在 Promise 解包类型上有什么工程应用?

  • 条件类型 + infer 解包 Promise 的实现
  • 嵌套 Promise 的递归解包
  • 与内置 Awaited 的差异与边界

MyAwaited 基础实现:type MyAwaited<T extends Promise<any>> = T extends Promise<infer U> ? (U extends Promise<any> ? MyAwaited<U> : U) : never——先解一层,若结果仍是 Promise 则递归;内置 Awaited 更完整:接受任意类型、处理 thenable({ then(onfulfilled: infer F): any })、null/undefined 透传、元组/数组递归解包。工程应用:其一,异步函数返回类型压平——泛型包装(重试、缓存、并发)中用 Awaited 推导最终值类型;其二,并发批处理——Promise.all 结果类型逐元素解包;其三,理解"解包语义"——写异步工具库时明确"解到非 Promise 为止"与 thenable 边界的差异。注意:MyAwaited 的约束 T extends Promise<any> 限制输入必须 Promise,比内置窄;嵌套与 thenable 场景推荐直接用内置 Awaited。

考察 Promise 解包的类型实现:infer + 递归,回答应说明实现层次与内置 Awaited 的能力边界。

#
★★

39. type MyReturnType 在函数返回类型推导的工程价值

type MyReturnType 如何实现?在函数返回类型推导上有什么工程价值?

  • infer R 提取返回值的实现
  • 重载与泛型函数的返回类型边界
  • 包装/代理函数的返回类型推导

MyReturnType 实现:type MyReturnType<T> = T extends (...args: any) => infer R ? R : never——匹配函数签名后 infer 返回值;边界:重载函数取最后一个签名(TS 的 Parameters/ReturnType 均如此)、泛型函数取"未实例化"的声明返回形态(无法内联展开)、非函数类型返回 never(约束可加 T extends Function 保护)。工程价值:其一,包装函数(memoize、throttle、once)返回类型从被包装函数推导,避免手写重复声明;其二,事件/插件系统中"处理函数返回类型"的统一提取(如中间件结果类型);其三,组合工具——ReturnType 与 Parameters/Awaited 组合推导异步回调的完整签名(Promise<Awaited<ReturnType<F>>>)。实践:用 Parameters 与 ReturnType 组合构建"签名适配器",类型随源函数同步。

考察返回值提取的实现与边界:infer 匹配、重载/泛型限制,回答应说明实现与包装场景价值。

#
★★

40. type MyExclude<T, U> 与 type MyExtract<T, U> 在联合类型筛选的工程应用

type MyExclude<T, U> 与 type MyExtract<T, U> 如何实现?在联合类型筛选上有什么工程应用?

  • 分布式条件类型的剔除/保留实现
  • 联合成员筛选的语义
  • 与 Pick/Omit、键筛选的组合

MyExclude 用分布条件类型:type MyExclude<T, U> = T extends U ? never : T——裸类型参数 T 触发分布,逐个成员判断"是否可赋给 U",命中的剔除(never),未命中保留;MyExtract 取反:T extends U ? T : never 保留命中成员。应用:其一,联合清理——从事件联合中剔除某事件(Exclude<EventUnion, 'click'>)、从配置键中剔除禁用项;其二,键集合运算——与 keyof 组合(Exclude<keyof T, K>)实现 Omit 的键剔除、白名单筛选;其三,按条件筛选成员——Extract<Union, { type: 'x' }> 与 LookUp 类似但按整体形状。要点:分布只对裸类型参数生效(元组包装 [T] 会阻止分布);联合中的 never 自动消失(空联合);条件中的 U 也参与判断(U 为联合时"可赋给任一成员"命中)。

考察分布条件类型的筛选机制:裸类型参数触发逐成员判断,回答应说明实现与键/成员筛选应用。

#
★★

41. type IsAny 、type IsUnknown 在边界类型守卫的工程价值

type IsAny 与 type IsUnknown 如何实现?在边界类型守卫上有什么工程价值?

  • IsAny 的 any 识别技巧
  • IsUnknown 的 unknown 识别
  • 检测"宽松类型泄漏"的工具化应用

IsAny 利用 any 与交叉的宽松性:type IsAny<T> = 0 extends (1 & T) ? true : false——若 T 是 any,1 & T 为 any,0 extends any 成立返回 true;其他类型(含 unknown)不成立。IsUnknown 用"是否仅 unknown 可赋":type IsUnknown<T> = IsAny<T> extends true ? false : (unknown extends T ? ([T] extends [unknown] ? true : false) : false)——unknown 可赋给 unknown 但 unknown 不可赋给其他类型(除 any 特殊),用"unknown extends T 且 T extends unknown"精确判定。工程价值:其一,宽松类型检测——工具类型/接口中检测"输入是否 any/unknown",防止宽松类型静默泄漏到精确契约(如类型测试断言"此工具不允许 any 输入");其二,守卫组合——IsAny/IsUnknown 组合实现对"未收窄边界"的编译期告警(配合 lint 或类型测试);其三,类型测试库内部依赖(expect-type 的 toBeAny/toBeUnknown 实现)。注意 any 与 unknown 的区分:可赋值层面 any 双向兼容,必须用 Equal 级技巧区分。

考察边界类型的检测技巧:any 的交叉宽松性、unknown 的赋值对称性,回答应说明实现与检测应用。

#
★★

42. type Mutable 与 type Required 在可变性与必填性的类型工具

type Mutable 与 type Required 在可变性与必填性上有哪些类型工具应用?

  • -readonly 与 -? 修饰符的映射
  • 可变/必填转换的深度边界
  • 与表单/配置模型的组合

Mutable 用 { -readonly [K in keyof T]: T[K] } 移除 readonly;Required 用 { -? [K in keyof T]: T[K] } 移除可选性(所有属性必填)——两个工具都只作用于当前层级(浅层),深层需 Deep 版本(递归 + 终止边界)。工程应用:其一,可变性建模——外部数据只读视图(Readonly)与内部编辑副本(Mutable)的类型分离,防止误改;其二,必填性建模——表单提交模型(Required 去除可选默认字段)、配置合并(合并后必填);其三,组合——Required<Mutable<T>> 表达"完全就绪"状态、与 Partial/Pick 组合表达部分更新模型。注意:Required 对"值为 undefined"的可选属性处理(-? 后变为必填但 undefined 联合保留与否取决于 exactOptionalPropertyTypes)、递归边界(函数/类实例不展开)。

考察修饰符映射的工具:-readonly/-? 与深层边界,回答应说明实现与建模组合。

#
★★

43. type TupleToUnion<T extends readonly any[]> 在联合推导的工程实践

type TupleToUnion<T extends readonly any[]> 如何实现?在联合推导上有什么工程实践?

  • T[number] 索引访问的联合推导
  • as const 数组与联合的联动
  • 配置/枚举的"数组即联合"模式

TupleToUnion 用索引访问:type TupleToUnion<T extends readonly any[]> = T[number]——数字索引联合取全部元素类型并集;配合 as const 数组(const STATUSES = ['idle', 'loading'] as const)得到字面量联合 'idle' | 'loading'。工程实践:其一,枚举替代——用数组(可迭代、可运行时使用)同时推导联合类型(type Status = TupleToUnion<typeof STATUSES>),避免 enum 的运行时对象与类型断开;其二,白名单联动——表单字段、路由段、权限角色以数组声明,联合与运行时遍历同源;其三,映射推导——TupleToUnion 结果驱动 Record 键(Record<TupleToUnion<T>, X>)与配置校验。要点:readonly 约束保证 as const 数组可匹配;元素类型任意(字面量、对象、函数);与 TupleToObject 的区别(转对象 vs 转联合)。

考察元组到联合的推导:T[number] 与 as const 联动,回答应说明"数组即类型源"的工程模式。

#
★★

44. type Last<T extends any[]> 在元组最后元素的类型推导

type Last<T extends any[]> 如何实现?元组最后元素的类型推导有什么应用?

  • infer 匹配尾元素的实现
  • 空元组与单元素元组的边界
  • 在参数栈、事件签名中的应用

Last 的实现:type Last<T extends any[]> = T extends [...infer _, infer L] ? L : never——rest 匹配前部、infer L 匹配尾元素;等价写法 T extends [infer _, ...infer Rest] 递归取最后(低效);索引技巧 T extends [...infer R, infer L] 同样适用。边界:空元组匹配失败返回 never(或 undefined 按需)、单元素元组返回该元素。应用:其一,参数栈——累积的参数元组取"最后添加的参数"类型(builder 流式 API 的状态推导);其二,事件/消息签名——变长参数的最后位置类型提取;其三,组合——与 Push/Pop 配合实现"栈式"类型变换(Push 追加 + Last 查看栈顶);注意 readonly 元组需 readonly any[] 约束或展开兼容。

考察尾元素提取的实现:rest + infer,回答应说明实现、空元组边界与栈式应用。

#
★★

45. type Pop<T extends any[]> 在元组移除末尾的类型工具

type Pop<T extends any[]> 如何实现?元组移除末尾的类型工具有什么应用?

  • rest 匹配去尾([...infer R, any])的实现
  • 空元组与单元素的处理
  • 撤销栈、历史记录的类型建模

Pop 实现:type Pop<T extends any[]> = T extends [...infer R, any] ? R : never——匹配"任意前部 + 末尾元素",返回前部 R(移除末尾);空元组匹配失败返回 never(或按约定返回空元组 [])。应用:其一,撤销/历史建模——操作栈类型(Pop 回退、Push 前进),配合 Last 查看栈顶、Length 判断空栈,实现类型级"可撤销状态机";其二,参数变换——从累积参数中移除最后参数(适配"去掉最后一个参数的回调");其三,组合工具——Pop 与 Concat/Push 组合实现元组任意位置的插入删除(先拆后拼)。边界:readonly 元组适配、单元素元组 Pop 返回空元组、rest 元素(any[])输入时 R 为数组退化。

考察元组去尾的实现与栈式应用:rest 匹配 + 组合变换,回答应说明实现与撤销栈建模。

#
★★

46. type Shift<T extends any[]> 在元组移除首元素的类型工具

type Shift<T extends any[]> 如何实现?元组移除首元素有什么应用?

  • rest 匹配去头([any, ...infer R])的实现
  • 空元组边界与返回类型约定
  • 队列/状态转移的类型建模

Shift 实现:type Shift<T extends any[]> = T extends [any, ...infer R] ? R : never——匹配"首元素 + 任意前部"(实际为"首元素 + 其余"),返回 R(移除首位);与 Pop 对称。应用:其一,队列建模——FIFO 类型队列(Shift 出队、Push 入队),配合 Last/First 查看两端;其二,状态转移——递归类型处理元组(如逐元素变换:Shift 取剩余 + First 处理头部),实现类型级遍历(对元组逐元素映射/判断);其三,参数处理——移除首参数后适配回调(柯里化、预绑定参数的类型表达)。边界:空元组返回 never(或约定 [])、单元素 Shift 返回空元组、rest 元素输入时 R 为数组。

考察元组去头的实现与应用:rest 匹配、队列与递归遍历,回答应说明实现与类型级遍历模式。

#
★★

47. 类型挑战中的 Pick<T, K> 与 Omit<T, K> 手写实现与内置工具类型的差异

类型挑战中的 Pick<T, K> 与 Omit<T, K> 手写实现与内置工具类型有什么差异?

  • 手写实现的映射与键约束
  • 内置实现的宽松行为(K 超界)
  • 差异对使用预期的影响

手写 Pick:type MyPick<T, K extends keyof T> = { [P in K]: T[P] }——K 被约束为 T 的键(超界直接报错),映射后保留对应属性;内置 Pick 的声明是 Pick<T, K extends keyof T>(同样约束,但实现经 Exclude/键重映射过滤,行为一致)。手写 Omit:type MyOmit<T, K> = { [P in keyof T as P extends K ? never : P]: T[P] }(键重映射剔除)或 Pick<T, Exclude<keyof T, K>>(组合实现)——注意手写版 K 可不约束(未命中键自然被忽略),内置 Omit 同宽松。差异点:其一,约束面——手写 Pick 的 K 约束报错时机与内置一致(extends keyof T),但若用宽松写法(不约束)则超界键返回 undefined 属性;其二,映射方式——键重映射 vs Exclude 组合,行为等价但可读性/性能有别;其三,分布/修饰符——映射保留 readonly/可选性(? 修饰符经映射保留),手写时若想调整需显式修饰符。工程上推荐直接用内置,手写用于理解机制。

考察工具类型的手写实现与内置差异:键约束、重映射 vs 组合、修饰符保留,回答应说明实现与行为等价性。

#
★★

48. 递归类型(type Flatten 、type DeepReadonly)在树形 JSON Schema 与 Redux 状态类型化的工程应用

递归类型(Flatten、DeepReadonly)在树形 JSON Schema 与 Redux 状态类型化中如何应用?

  • Flatten 的数组/嵌套压平实现
  • DeepReadonly 的状态树只读化
  • Redux 状态与 JSON Schema 的递归建模

Flatten 递归压平嵌套数组:type Flatten<T> = T extends any[] ? (T extends [infer F, ...infer R] ? [...Flatten<F>, ...Flatten<R>] : []) : [T]——元素若为数组继续压平、否则保留;DeepReadonly 递归只读化整个树。工程应用:其一,JSON Schema——树形 schema(properties/items 递归)用递归类型建模节点,配合类型级校验(字段存在性、类型匹配),schema 类型与数据形状同步;其二,Redux 状态——DeepReadonly<State> 把整个状态树标为只读,reducer 外禁止修改,配合"状态不可变契约"的编译期强制;切片状态(SliceState)递归建模嵌套字段;其三,Selectors——从状态树逐层取数的 selector 返回值类型随递归建模精确推导。要点:递归终止(原始类型/函数/Date)、数组/元组分支、大状态树的深度与推断性能治理。

考察递归类型在数据建模的应用:压平/只读化与 schema/状态树,回答应说明实现分支与工程约束。

#
★★

49. Brand Types(type UserId = string & { __brand: 'UserId' })在表单状态与领域模型的工程价值

Brand Types 在表单状态与领域模型中有哪些工程价值?

  • 品牌类型的构造与转换
  • 表单字段 ID 与实体 ID 的隔离
  • 领域模型中"同构不同义"的治理

表单与领域模型常出现"同构不同义":UserId 与 FormId 都是 string、表单字段 key 与实体字段 key 都是 string,混传时编译器无感。Brand Types 用 string & { __brand: 'X' } 隔离:type FormFieldKey = string & { __brand: 'FormFieldKey' },表单 API 的参数声明品牌类型后,外部 string 必须经显式转换(工厂/守卫)才能传入,杜绝"把实体 ID 当表单 key"这类错传。工程价值:其一,ID 语义隔离——实体 ID、请求 ID、索引 key 各自品牌化,接口参数顺序错误编译期拦截;其二,表单状态建模——表单字段名/值域用品牌 + 字面量联合约束,动态字段(record 键)安全收窄;其三,领域模型边界——DTO 转换点(schema 校验后)品牌化进入领域层,内部强类型;代价:转换样板与跨包品牌重建,用于高风险边界而非全部 string。

考察品牌类型在业务建模的价值:同构值语义隔离、转换点强制,回答应说明实现与表单/领域应用。

#
★★

50. 条件类型与 infer 在 Promise 解包、函数返回类型推断、数组元素类型提取的工程实践

条件类型与 infer 在 Promise 解包、函数返回类型推断、数组元素类型提取中有哪些工程实践?

  • 三类提取的实现模式
  • 递归与组合的实践
  • 异步/回调/集合场景的类型安全

三类提取共用"条件类型 + infer"模式:Promise 解包——T extends Promise<infer U> ? ...(递归得 Awaited);函数返回类型——T extends (...args: any) => infer R ? R : never;数组元素——T extends (infer E)[] ? E : never(元组用 T[number])。工程实践组合:其一,异步 API 包装——泛型函数接收 async 函数,用 Parameters<F>/ReturnType<F>/Awaited<...> 推导完整签名(async function withRetry<F extends (...args: any) => any>(fn: F): Promise<Awaited<ReturnType<F>>>),包装后调用方类型零损失;其二,集合处理——数组元素的精确提取(ElementOf<T>)用于集合工具的泛型签名(find/filter 的类型化包装);其三,事件载荷——Promise<infer U>(infer E)[] 嵌套提取复杂结构(异步数组的最终元素类型)。要点:递归终止、分布条件类型(裸参数)、联合输入的逐成员处理。

考察 infer 三类提取的实践组合:异步包装、集合工具与载荷提取,回答应说明实现模式与组合用法。

#
★★

51. 类型编程在 RPC 客户端(tRPC、Hono Client)

类型编程在 RPC 客户端(tRPC、Hono Client)中有哪些工程实践?

  • 过程/路由类型到客户端调用的推导
  • 输入输出类型的端到端同步
  • 错误与上下文的类型化

tRPC 把服务端过程(procedure)类型化:createTRPCRouter 中每个 query/mutation 的输入 schema(infer 出输入类型)与输出类型(infer 出返回类型)构成"过程类型",客户端 trpc.user.list 的类型(参数、返回值、错误)由服务端类型自动推导——新增/修改接口后客户端签名同步变化,编译期拦截协议漂移;实现依赖:泛型路由器类型(Router 类型递归映射所有过程)、输入/输出经 zod infer、错误类型(TRPCError)并入过程签名。Hono Client(hono 的 RPC 模式)类似:用路由 handler 的类型定义 typeof routes 推导客户端调用(hc<AppType>client.get('/user/:id') 参数/响应类型),路径参数(模板字面量)与响应 schema 类型联动。工程实践:契约单源(schema/路由定义即类型)、共享类型包跨端复用、类型测试固化关键过程签名、错误处理按类型分支。

考察 RPC 类型驱动的端到端契约:过程类型映射、schema infer 与客户端推导,回答应说明机制与工程收益。

#
★★

52. 类型级 Schema 校验(zod、valibot、typia)

类型级 Schema 校验(zod、valibot、typia)各有什么特点?如何选型?

  • 三者的实现路线(运行时解释 vs 编译期代码生成)
  • 性能、体积与类型体验的差异
  • 选型依据(性能敏感、体积敏感、生态)

三条路线:zod——运行时解释执行:schema 是运行时对象,parse 逐字段校验,infer 导出类型;API 直观、生态最大,但解析性能与体积相对大(每字段函数调用)。valibot——同样运行时解释,但按"原子 schema"模块化设计,支持 tree-shaking(只打包用到的校验器),体积极小、性能与 zod 相当或略优,API 风格与 zod 接近(zod v3 兼容层)。typia——编译期代码生成:通过 Transformer 在构建时把"纯类型标注"生成为校验代码,运行时是手写级性能(比解释型快 10-100 倍)、零 schema 定义(直接用 interface 类型)、体积小;代价:需要编译器插件(ts-patch/ts-node 或打包器集成)、动态/复杂逻辑受限。选型:性能敏感(高频解析、服务端)与"类型即 schema"偏好用 typia;体积敏感(客户端打包)用 valibot;生态与渐进(表单、tRPC 集成)用 zod。三者都支持 infer 类型与自定义校验。

考察 schema 库的实现路线差异:解释型 vs 代码生成,回答应说明性能/体积/生态维度与选型。

#
★★

53. 类型级 Builder Pattern 在 type BuildRequest 等链式调用类型推导的工程实践

类型级 Builder Pattern(如 type BuildRequest)在链式调用类型推导上有哪些工程实践?

  • 泛型状态累积的链式类型设计
  • 方法可用性随状态收窄
  • 请求构建、表单编排的应用

类型级 Builder 用"状态泛型累积"设计:type BuildRequest<T = {}> = { withAuth(this: BuildRequest<T & { auth: Auth }>, a: Auth): BuildRequest<T & { auth: Auth }>; send(this: BuildRequest<T & { auth: Auth; body: Body }>): Promise<Resp> }——每步方法把新字段并入累积状态 T 并返回新状态类型,send 只在"已设置 auth 与 body"的状态下可用(this 参数约束当前状态),未设置时调用 send 编译报错,实现"必需步骤强制顺序"的类型状态机。工程实践:其一,请求构建——强制必填字段(auth/body/headers)按序设置,可选步骤任意顺序,最终发送类型携带完整状态;其二,表单编排——步骤向导(step1 → step2 → submit)类型强制;其三,配置构建——必选项与默认值(默认值并入状态后不可覆盖或标注覆盖点)。要点:this 参数约束状态、返回新状态(不可变累积)、状态联合(T & X)顺序敏感、中间类型可具名导出便于调试;结合类(class Builder)或函数(curried)两种形态。

考察链式 API 的类型状态机:泛型累积 + this 约束可用性,回答应说明设计模式与工程应用。

#
★★

54. 映射类型的键重映射(as clause, TS 4.1+)在事件总线类型与 Redux Action 的现代应用

映射类型的键重映射(as clause)在事件总线类型与 Redux Action 中有哪些现代应用?

  • as 子句的键变换能力(过滤/改名/条件)
  • 事件名到载荷/处理器类型的映射
  • Action 联合的自动派生

键重映射在事件与 Action 建模中的典型应用:其一,事件总线——由事件映射派生"处理器签名":type Handlers<T> = { [K in keyof T as \on${Capitalize<K & string>}`]: (payload: T[K]) => void }——事件名自动转处理器名(click → onClick),载荷类型联动;其二,事件载荷表——{ [K in keyof Events]: Events[K] extends { payload: infer P } ? P : never } 按条件映射载荷;其三,Redux Action——由 action creator 表派生 Action 联合:type Actions = { [K in keyof Creators]: ReturnType<Creators[K]> }[keyof Creators](值映射 + 索引访问取联合),reducer 中按 type 判别收窄;或键重命名(as `${K & string}Request`)生成请求/成功/失败三态 action 名。要点:as 后可接条件(as never 过滤)、模板字面量改名、修饰符(readonly/?);键的 string 化(K & string`)避免 symbol/number 键参与模板拼接。

考察键重映射在事件/Action 建模的现代应用:处理器名派生、载荷映射与 Action 联合,回答应说明 as 子句的变换组合。

#
★★

55. 自定义 transformer(before/after/afterDeclarations)与 ts-patch/ts-node 的编译时 AST 改写边界

自定义 transformer(before/after/afterDeclarations)与 ts-patch/ts-node 的编译时 AST 改写边界是什么?

  • transformer 的类型(before/after/afterDeclarations)与时机
  • ts-patch 对 tsc 的补丁、ts-node 的 transformer 注入
  • 改写边界(源码生成、声明生成、类型检查)

transformer 是"编译期 AST 改写"的钩子:before——在类型检查与 emit 前运行(改动影响检查与产物,适合源码转换如 enum 替换、AOT 生成);after——类型检查后、emit 时(只影响产物,不影响检查结果,适合产物优化);afterDeclarations——声明文件生成阶段(改写 .d.ts,适合声明增强/裁剪)。注入方式:tsc 本身不直接支持自定义 transformer,需 ts-patch(patch tsc 的 transformer 钩子,通过 tsconfig 的 customTransformers 字段或编程 API)或打包器集成(ts-loader/babel 插件);ts-node 的 compilerOptions/transformers 接口支持编程注入(配 swc 时用 swc 插件体系)。边界:其一,before transformer 的输出参与类型检查,改写错误会引入类型错误(需保证生成代码类型正确);其二,after transformer 不影响检查——产物与检查可能不一致(生成代码绕过类型检查);其三,声明改写需 afterDeclarations 且要保证声明与产物一致;其四,ts-patch 依赖 tsc 内部 API,TS 版本升级可能破坏;与 erasableSyntaxOnly/isolatedDeclarations 的静态约束相比,transformer 是"动态改写"的兜底手段,应控制使用面。

考察 transformer 的时机与注入边界:三阶段影响面、ts-patch 机制与一致性风险,回答应说明改写边界的取舍。

#
★★

56. TSDoc/JSDoc 注释提取与 TypeChecker.getSignatureFromSymbol 在文档自动化中的工程应用

TSDoc/JSDoc 注释提取与 TypeChecker.getSignatureFromSymbol 在文档自动化中如何应用?

  • TSDoc 注释的解析(@param/@returns/@remarks)
  • getSignatureFromSymbol 获取函数/方法签名信息
  • API 文档自动生成与校验

文档自动化管线:用 TypeScript Compiler API 遍历导出符号(getExportedSymbols/遍历 SourceFile),对每个符号用 JSDoc/TSDoc 解析器(@microsoft/tsdoc 解析注释为结构化模型:@param、@returns、@remarks、@deprecated、@example 等标签),配合 TypeChecker.getSignatureFromSymbol(函数/方法符号 → Signature,含 parameters、returnType、typeParameters)与 getTypeOfSymbolAtDeclaration 提取完整签名,生成文档数据(名称、签名、参数类型、返回值、注释),输出 Markdown/JSON(或对接 api-documenter)。工程应用:其一,API 文档自动生成——库的文档站点与源码同源,签名与注释同步;其二,文档校验——TSDoc 规则(必需标签、链接有效性)用 lint 或构建脚本检查,CI 把关;其三,变更报告——文档 diff 与 API Extractor 的 api-report 联动,注释缺失(导出无 @remarks)也纳入检查。要点:注释提取需正确解析 TSDoc 语法(不兼容 JSDoc 的标签语法差异)、泛型与重载签名需逐个 signature 处理。

考察文档自动化的编译器管线:注释解析 + 签名提取,回答应说明 API 与生成/校验流程。

#
★★

57. Declaration Merging 与 Module Augmentation 在扩展第三方库类型声明中的工程取舍

Declaration Merging 与 Module Augmentation 在扩展第三方库类型声明中如何取舍?

  • 声明合并(interface/namespace)与模块增强的机制
  • 全局扩展与模块扩展的选择
  • 扩展的维护与冲突治理

扩展第三方库类型的两种通道:模块增强(module augmentation)——declare module 'lib' { interface X { newField: T } } 合并到库的导出接口,只影响该模块内类型;全局声明合并——declare global { interface Window { ... } } 或环境文件中的顶层声明扩展全局。取舍:能合并的前提是目标类型用 interface(或 namespace)声明——库用 type 定义则无法合并(需另写包装类型或改库);优先模块增强(局部、可迁移、不影响全局命名空间),全局扩展仅用于确实的全局对象(window、process 环境扩展);多包同时增强同一接口会冲突(类型面相互叠加),需组织好"扩展文件"(类型补丁包)并纳入类型入口;增强与库版本升级联动(库升级后增强可能失效或语义变化),用类型测试固化增强行为。工程实践:集中式"类型补丁"目录(每个第三方库一个 .d.ts)、CI 类型检查把关、升级依赖时回归增强。

考察扩展第三方类型的机制与取舍:模块增强 vs 全局合并、interface 前提与冲突治理,回答应说明选型与维护。

#
★★

58. tsc --build 与 project references 在 monorepo 增量编译中的依赖图管理与缓存策略

tsc --build 与 project references 在 monorepo 增量编译中的依赖图管理与缓存策略是什么?

  • references 依赖图与构建顺序
  • .tsbuildinfo 缓存的失效条件
  • 增量策略(--force、--clean、并行)

依赖图管理:每个子项目 tsconfig 用 references 声明依赖("references": [{ "path": "../pkg-types" }]),tsc --build 读取全部 referenced 项目递归构建,按拓扑序(被依赖先构建);构建某项目时先确保其依赖产物(.d.ts + .tsbuildinfo)最新,变更传播按依赖边传播(依赖变了下游重建)。缓存策略:.tsbuildinfo(含文件哈希、版本、选项)判断"是否需要重建"——仅当源文件、依赖产物或配置变化时才重编译该包;失效场景:源文件修改、依赖包变更、tsconfig 选项变化、--force 强制全量、--clean 清理缓存。实践:根 tsconfig 用 references 聚合全仓,CI 执行 tsc --build(增量,快)+ 定期 tsc --build --force(全量校验缓存正确性);--watch 增量监听开发;多项目并行(--build 支持并行构建独立分支);排除循环引用(构建会报错);与打包器/IDE 的 TS Server 协作(IDE 自动加载 references 项目)。

考察增量编译的依赖图与缓存模型:拓扑构建、缓存失效条件与命令策略,回答应说明管理与实践。

#

59. TypeScript Language Service Plugin 在 IDE 中提供自定义诊断与补全的工程边界

TypeScript Language Service Plugin 如何在 IDE 中提供自定义诊断与补全?工程边界是什么?

  • Language Service Plugin 的钩子(getSemanticDiagnostics/getCompletionsAtPosition)
  • 插件激活与配置(plugins 字段)
  • 诊断/补全扩展的能力边界

Language Service Plugin 通过 tsconfig 的 "plugins": [{ "name": "my-plugin" }] 激活(或编辑器 settings),插件实现 LanguageService 包装器:create(info: ts.server.PluginCreateInfo) 返回增强后的 service,重写钩子——getSemanticDiagnostics(自定义语义诊断,如样式规范检查、特殊类型约束)、getCompletionsAtPosition(自定义补全,如组件 props 补全、模板字符串片段补全)、getQuickInfoAtPosition(悬停增强)、getCodeFixesAtPosition(自定义修复)等,未重写的方法委托原 service。能力边界:其一,插件运行在 TS Server 进程内,可访问 Program/TypeChecker(类型感知诊断);其二,诊断/补全只影响 IDE(不参与 tsc 构建,构建期需另配检查器);其三,需随 TS 版本兼容(LS API 变化);其四,性能敏感(每个文件每个位置的钩子调用,需缓存与剪枝);工程应用:团队规范诊断(禁止模式)、领域 DSL 的补全(CSS-in-JS 类名、i18n key)、框架模板的类型感知提示。

考察 LS 插件的扩展机制:钩子重写、类型感知与 IDE-only 边界,回答应说明激活方式、能力与限制。