类型基础与工具类型

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

1. exactOptionalPropertyTypes 在库发布与消费的类型一致性

exactOptionalPropertyTypes 是什么?它在库发布与消费场景中如何影响类型一致性?开启后有哪些需要注意的边界?

  • exactOptionalPropertyTypes 开启后可选属性与显式 undefined 的严格区分
  • 对库作者类型声明与消费者使用方式的约束
  • 与 strict 家族其他选项的协作及迁移成本

exactOptionalPropertyTypes 开启后,可选属性(prop?: T)只允许"属性缺失",不允许显式赋值为 undefined;未开启时 prop?: T 等价于 prop?: T | undefined。这一差异在库发布场景影响重大:库作者声明的可选回调、配置项在消费者传入 undefined 时会被拒绝,迫使消费者用条件判断或 ?? 兜底,从而在编译期暴露真实的 undefined 传播路径。对库消费者而言,开启后必须显式处理"属性存在但值为 undefined"与"属性缺失"两种状态,避免把 undefined 当作"未传"滥用。

本题考察对可选属性语义精确化的理解:exactOptionalPropertyTypes 不是简单的严格模式开关,而是把"缺省"与"显式 undefined"两种状态在类型层面分离,这对库 API 的契约设计和消费端的赋值习惯都有直接影响,回答时应说明其意义、影响面与迁移注意事项。

#
★★★

2. DeepReadonly/DeepPartial 在外部 API 接入的类型边界

DeepReadonly 与 DeepPartial 在外部 API 接入场景中解决什么问题?它们与浅层 Readonly/Partial 在类型边界上有何差异?

  • 浅层与深度递归工具类型的区别
  • 外部 API 响应数据只读建模的意义
  • 递归映射类型在联合类型、函数类型上的边界

外部 API 返回的嵌套 JSON 结构用浅层 Readonly 只能冻结第一层,深层字段仍可被意外修改;DeepReadonly 通过递归映射类型把每一层属性都变为 readonly,在类型层面强制表达"响应数据不可变"的契约,防止业务代码误写。DeepPartial 则递归地把所有层级字段变为可选,常用于部分更新请求体(PATCH)或测试桩构造。二者的实现依赖递归条件类型,对函数类型、Date 等特殊类型需谨慎处理,避免把函数签名或类实例错误地递归展开。

考察递归工具类型对嵌套结构的覆盖能力:外部 API 数据通常是多层 JSON,浅层工具类型存在盲区,Deep 系列把不可变/可选语义递归下沉,回答时需点出递归终止条件与特殊类型(函数、数组、类)的处理边界。

#
★★★

3. enum、const enum 与 as const 对象的取舍,运行时存在性、tree-shaking 与类型推导的工程差异

enum、const enum 与 as const 对象在运行时存在性、tree-shaking 与类型推导上有哪些工程差异?如何取舍?

  • 普通 enum 会生成运行时对象、const enum 编译期内联
  • as const 对象加 typeof/satisfies 的现代替代方案
  • tree-shaking、isolatedModules 与命名空间限制下的取舍

普通 enum 会生成一个运行时对象并支持反向映射(数字枚举),但产物不便于 tree-shaking 且在有 isolatedModules 或 verbatimModuleSyntax 的打包链中可能与编译器内联行为冲突;const enum 编译期直接内联字面量、产物零开销,但独立编译(Babel/esbuild)下无法正确转译,需要 preserveConstEnums 或直接禁用。as const 对象加 typeof 与 keyof 提取联合类型,既保留字面量推导与运行时对象的可 tree-shaking 特性,又避免 enum 的语法噪音,是当前库与业务代码的主流替代方案。

考察三类枚举方案在"运行时对象是否存在、产物能否摇树、类型推导是否精确"三个维度的差异:普通 enum 有运行时对象,const enum 依赖编译器内联与打包链协作,as const 对象在产物与类型两个层面都更可控,回答应给出按工程链路(tsc 全量编译 vs 混合转译器)选择的依据。

#
★★★

4. 协变(covariance)、逆变(contravariance)与 strictFunctionTypes 在函数类型兼容性中的工程影响

什么是协变与逆变?strictFunctionTypes 开启后对函数类型兼容性有什么影响?在工程中如何理解这些规则?

  • 函数参数逆变、返回值协变的基本规则
  • strictFunctionTypes 只对函数类型参数启用严格逆变检查
  • 方法(method)声明与函数属性(property)声明的检查差异

类型系统的变体规则描述复合类型在子类型关系下的方向性:返回值是协变的(子类型返回值可以替换父类型返回值),参数是逆变的(参数类型需为父类型才能安全替换)。TypeScript 出于实用性默认对参数使用双变(bivariant)检查,strictFunctionTypes 开启后对"函数类型"(function type)的参数改为严格逆变,但"方法声明"(method)仍保持双变以兼容 Array 等内置类型。工程影响:开启后事件回调、配置函数等场景的赋值可能报错,需要显式声明更宽泛的参数类型或使用 satisfies/映射类型调整。

考察对变体规则与严格模式开关的准确理解:strictFunctionTypes 不是简单"变严",而是只针对函数类型属性收紧参数检查,方法声明保持双变;回答应结合赋值安全的直觉解释"参数逆变、返回协变",并说明该选项在回调型 API 中的实际影响。

#
★★★

5. Template Literal Types(${Uppercase } ${Uncapitalize} 等)

Template Literal Types 是什么?${Uppercase }、${Uncapitalize} 等内置字符串操作类型如何工作?有哪些应用?

  • 模板字面量类型在字符串模式上的字面量推导
  • 内置 intrinsic string manipulation 类型(Uppercase/Lowercase/Capitalize/Uncapitalize)
  • 在路由、事件名、CSS-in-JS 等场景的模式匹配应用

Template Literal Types 用反引号加 ${T} 插值把字符串模式编码为类型,推断时会对字符串字面量做模式匹配与分解,例如 ${string}-${string} 可约束"含连字符"的字符串。TS 4.1 提供 Uppercase、Lowercase、Capitalize、Uncapitalize 四个内置字符串操作类型,它们由编译器内置实现,可在模板字面量中组合使用,例如 ${Uppercase<T>}_KEY 生成全大写键名。应用场景包括:由事件名拼接生成事件处理器映射、由路由模式推导参数对象、由常量前缀生成 i18n 键、在 CSS-in-JS 中推导原子类名等。

考察模板字面量类型把"字符串值空间"提升为可计算的"类型空间"的能力:关键是理解 ${T} 插值会在推断时做模式匹配,配合四个内置字符串操作类型可以生成或分解字符串字面量,从而把字符串约定变成编译期约束。

#
★★★

6. infer 在解构函数参数、Promise resolve 类型、数组元素的深度应用

infer 关键字在条件类型中如何工作?在解构函数参数、Promise resolve 类型与数组元素提取上有哪些深度应用?

  • infer 在条件类型中的声明与匹配机制
  • 函数参数、返回值、Promise、数组元素的提取
  • infer 与递归、联合分布的组合使用

infer 只能在条件类型的 extends 分支中声明一个待推断的类型变量,编译器在匹配时根据实际结构回填。典型应用:Parameters<T>T extends (...args: infer P) => any ? P : never 提取函数参数元组,ReturnType<T> 提取返回值,Awaited<T> 递归解包 Promise,T extends (infer U)[] ? U : never 提取数组元素类型。更复杂的场景可结合递归与分布条件类型,例如深度解包嵌套 Promise、逐层提取元组元素、在函数重载中取最后一个签名等,是编写高级工具类型的核心手段。

考察 infer 作为"类型提取器"的本质:它借由模式匹配回填变量,使条件类型从"判断"升级为"拆解结构",回答应覆盖函数、Promise、数组三类基础提取并说明递归场景,体现对推断机制的理解深度。

#
★★★

7. Record<K, V> 在字典对象与 i18n 文案映射的工程化应用

Record<K, V> 工具类型如何工作?在字典对象与 i18n 文案映射场景中有哪些工程化应用?

  • Record<K, V> 的映射类型实现本质
  • 字典对象键约束与值类型统一
  • i18n 文案映射与 keyof 联合的配合

Record<K, V> 本质是 type Record<K extends keyof any, T> = { [P in K]: T },把键集合 K(通常为字符串/数字/符号联合)映射为值类型 T 的对象。工程价值:其一,用联合类型做键约束,拼写错误在编译期暴露;其二,统一字典值类型,避免散落 any;其三,配合 typeof import('./messages') 与 keyof 提取,可让 i18n 文案映射的 key 与内容自动保持同步,新增语言包缺 key 时立即报错,实现"文案即类型契约"。

考察映射类型在"键集合约束 + 值统一"上的工程价值:Record 的要点不是实现本身,而是用键联合与值类型把字典、i18n 表这类数据结构的形状固化到类型系统,回答应点出 keyof 联动与编译期完整性校验。

#
★★★

8. Readonly vs Immutable 与 React Props/State 不可变契约

Readonly 与 Immutable 有什么区别?React Props/State 的不可变契约如何用类型表达?

  • Readonly 浅层只读与 Immutable 库深度不可变的差异
  • 编译期只读 vs 运行时冻结
  • React Props/State 更新的不可变原则与类型表达

Readonly 是 TS 内置的浅层只读,只冻结属性赋值的类型检查,不递归、不冻结运行时对象;Immutable (immutable.js 的 Record/List/Map 类型)把不可变下沉为运行时数据结构,任何"修改"都返回新实例。React 的 Props/State 不可变契约是运行时约定(不可直接赋值修改 state,须用 setState 产生新值),在类型层面通常用 Readonly 、Readonly 表达"组件外部不可写";若要更强约束,可用 DeepReadonly 或配合 ESLint 规则禁止直接 mutation。工程上应根据"约束强度"与"运行时成本"选择:纯类型层 Readonly 零成本但只在编译期生效,Immutable 数据结构有运行时收益也有迁移成本。

考察"编译期类型约束"与"运行时不可变结构"两个层次的差异:React 的不可变契约本质是约定而非类型强制,Readonly 只提供浅层编译期保护,回答应区分二者并说明在 React 场景的组合使用方式。

#
★★★

9. 模板字面量类型在路由与权限字符串模式构建中的应用

模板字面量类型如何在路由与权限字符串模式构建中应用?能带来哪些类型收益?

  • 用模板字面量约束路由路径与权限字符串格式
  • 由模式推导参数对象与权限组合
  • 与 as const、satisfies 配合的实践

路由场景中可用 type Route = \/user/${string}/:id`或更精确的${'get'|'post'} ${path}模式约束字符串,再通过 infer 从字面量中提取:id参数形成{ id: string }参数对象,实现"路由注册即参数类型推导"(tRPC、Elysia 等库的核心手段)。权限场景中可定义${Action}_${Resource}${Scope}:${Permission}` 的组合模式,用模板字面量类型枚举合法权限字符串,配合联合类型做编译期白名单。实际工程常与 as const 数组、satisfies 校验配合,保证配置表与推导出的联合类型同步演化。

考察模板字面量类型在"字符串领域约定"建模中的应用:核心收益是把散落的字符串模式收敛为可推导、可枚举的类型,回答应展示"由模式到参数/权限联合"的推导路径,并说明与 as const、satisfies 的组合。

#
★★★

10. 映射类型与键重映射(as 子句)的转换

映射类型如何工作?键重映射(as 子句)能实现哪些转换?

  • 映射类型 [K in keyof T] 的迭代机制
  • as 子句的键过滤、键改名与条件保留
  • 与模板字面量类型、条件类型的组合

映射类型通过 [K in keyof T] 迭代源类型的键并生成新对象类型,可配合修饰符(readonly、?)调整属性特性。键重映射(TS 4.1+)在 as 子句中对每个键做转换:可用 as never 过滤键、用模板字面量改键名(如 \get${Capitalize}`)、用条件类型按值类型选择性保留键,还能在映射中配合 ?/-?` 修饰符调整属性的可选性。典型应用:Pick/Omit/Partial 的手写实现、Getters/Setters 生成、事件名到方法名的映射、按值类型筛选属性等。

考察映射类型从"逐键复制"到"键级变换"的完整能力:as 子句让映射从值变换升级为键变换,配合模板字面量与条件类型可实现过滤、改名、派生等复杂对象变换,回答应覆盖三类典型转换并给出手写工具类型的思路。

#
★★★

11. Parameters /ConstructorParameters 在泛型库 API 中的边界

Parameters 与 ConstructorParameters 如何实现?在泛型库 API 设计中有哪些边界?

  • 函数参数元组与构造函数参数元组的提取
  • 泛型函数参数提取的边界(泛型函数内部无法获取具体参数)
  • 在库 API 包装、事件总线签名推导中的应用

Parameters 用 T extends (...args: infer P) => any ? P : never 提取函数参数元组,ConstructorParameters 用 T extends abstract new (...args: infer P) => any ? P : never 提取构造函数参数。边界:二者只能作用于具体的函数/构造函数类型,泛型函数(如 <T>(x: T) => T)在未实例化时无法推断具体参数;重载函数取最后一个签名;函数类型若包含 this 参数会出现在元组首位。泛型库中常用于包装 API(如节流、防抖、事件监听器注册),通过 Parameters 保留原函数签名实现参数透传与类型安全。

考察参数元组提取的实现与适用前提:理解 infer 匹配的具体签名边界(泛型函数、重载、this 参数)是设计泛型库时避免类型失真的关键,回答应同时给出实现与限制。

#
★★★

12. 条件类型与 infer 的类型提取能力

条件类型如何工作?它与 infer 结合能实现哪些类型提取能力?

  • 条件类型的判断结构与联合类型分布
  • infer 在判断分支中的类型提取
  • 递归条件类型的深度应用

条件类型 T extends U ? X : Y 在类型层面做可赋值性判断,遇到裸类型参数(naked type parameter)时会对联合类型做分布式展开(distributive),每个成员单独判断后结果重组为联合。结合 infer 可实现结构拆解:从 Promise 解出 resolve 类型、从函数拆出参数与返回值、从元组拆出首尾元素、从模板字符串拆出模式片段。递归条件类型(如 Awaited、DeepReadonly 的实现)配合终止条件([T] extends [never] 等)可处理任意深度嵌套结构,是类型编程的核心引擎。

考察条件类型的"判断 + 提取 + 递归"三能力:分布式条件类型、infer 模式匹配、递归终止条件是高级工具类型的地基,回答应说明分布式的触发条件(裸类型参数)与递归的终止策略。

#
★★★

13. moduleDetection 在测试与 CI 的工程价值

moduleDetection 配置是什么?它在测试与 CI 场景中有哪些工程价值?

  • moduleDetection 的 auto/legacy/force 三档语义
  • 文件被识别为模块还是脚本对全局命名空间的影响
  • 测试与 CI 中类型检查一致性保障

moduleDetection 控制"文件是否被视为模块":auto(默认)结合是否含 import/export 与 module 设置(esnext/preserve 等 ESM 风格)综合判断;legacy 模拟旧版行为,文件是否视为模块仅由是否含顶层 import/export 决定,module 编译选项不影响模块判定;force 强制所有文件为模块。在测试与 CI 中,auto 模式下无 import/export 的脚本文件会共享全局作用域,容易产生全局类型污染与声明冲突;CI 中不同文件集合(如仅编译测试文件)可能导致模块判定漂移,使本地通过而 CI 报错。强制 force 或显式配置可保证"每个文件独立模块"的一致性,配合 noUnusedLocals 等规则让类型检查结果可复现。

考察模块边界判定对类型检查一致性的影响:模块与脚本的判定差异会导致全局变量、declare global 的可见性不同,在 CI 环境(干净检查)与本地增量编译间产生差异,回答应说明三档语义并突出可复现性价值。

#
★★★

14. Awaited 、PromiseSettledResult 、Uppercase 等工具类型的边界与组合

Awaited 、PromiseSettledResult 、Uppercase 等工具类型各自解决什么问题?它们之间如何组合?

  • Awaited 递归解包 Promise 的语义
  • PromiseSettledResult 表达 Promise.allSettled 的联合结果
  • 字符串操作类型与映射类型的组合

Awaited 递归解包 Promise 与 thenable,直到得到非 Promise 类型,是 Promise.all/async 函数返回值类型推导的基石;PromiseSettledResult 是 { status: 'fulfilled'; value: T } | { status: 'rejected'; reason: any } 的判别联合,与 Promise.allSettled 返回类型对应,用 status 判别后可在类型层面安全访问 value/reason。Uppercase 等字符串操作类型作用于字面量类型,可与其他工具组合,如 Uppercase<keyof T> 生成键名大写映射。组合示例:Promise.allSettled 的结果数组用 PromiseSettledResult<Awaited<T>>[] 精确建模异步批处理。

考察工具类型的"单一职责 + 组合":Awaited 处理异步层级、PromiseSettledResult 处理结果状态、Uppercase 处理字符串形式,三者可在异步+字符串混合场景组合使用,回答应体现对各自边界的理解。

#
★★★

15. 模板字面量类型与 intrinsic string manipulation(TS 4.1+)的边界

模板字面量类型与 intrinsic string manipulation 类型有哪些边界?使用中需要注意什么?

  • 模板字面量类型只能约束字面量与模式,不能约束任意运行时字符串
  • Uppercase/Lowercase/Capitalize/Uncapitalize 由编译器内置实现
  • 推断复杂度与 union 展开的规模边界

模板字面量类型把"字符串形状"编码进类型,但它只对字面量类型与可推导的模式有效:变量(string 宽类型)无法通过模板字面量收窄,除非先用类型守卫收窄到字面量。四个字符串操作类型是编译器内置的 intrinsic 类型,不能自行实现(自定义 type Uppercase<T> = ... 会与内置冲突),且只接受字符串类型。工程边界:过长的模板字面量组合、大规模 union 交叉展开会显著拖慢推断甚至触发"类型实例化过深"错误,应避免在热路径类型上做深递归字符串分解,必要时用运行时验证兜底。

考察模板字面量类型的能力边界与性能边界:它适用于"字面量模式约束",但对宽 string 无能为力,且字符串操作类型不可自定义实现,回答应同时覆盖语义边界与性能治理。

#
★★★

16. Mutable /DeepMutable 在表单状态可变建模的应用

Mutable 与 DeepMutable 如何实现?在表单状态可变建模中有哪些应用?

  • 去除 readonly 修饰符的映射类型
  • 与 DeepReadonly 对称的深度移除
  • 表单状态"内部可变、外部只读"的类型建模

Mutable 通过 type Mutable<T> = { -readonly [K in keyof T]: T[K] } 移除顶层 readonly;DeepMutable 递归处理嵌套对象(函数、类实例需跳过或特殊处理)。表单场景的典型建模:全局配置、初始值用 DeepReadonly 表达不可变契约,而表单草稿对象用 Mutable/DeepMutable 表达"可在内部任意修改";组件对外暴露只读视图、内部维护可变副本,类型系统即可防止误把可变对象传给只读消费方或反向操作,同时保留表单编辑的灵活性。

考察 readonly 修饰符的映射反转:Mutable 是 Readonly 的逆操作,工程价值在于用类型区分"可变域"与"不可变域",回答应说明递归实现的边界(函数、类、Date)与表单双层建模方式。

#
★★★

17. Awaited 与 Promise.all 返回值类型推导的工程价值

Awaited 工具类型与 Promise.all 返回值类型推导有什么关系?有哪些工程价值?

  • Awaited 递归解包 Promise/thenable
  • Promise.all 对元组与数组的不同推导
  • 异步并发批处理的精确类型建模

Promise.all 接收 Promise 值数组/元组,返回值为"每个元素解包后的类型",其类型推导依赖递归解包逻辑——Awaited 正是这一语义的标准实现:type Awaited<T> = T extends null | undefined ? T : T extends object & { then(onfulfilled: infer F): any } ? F extends (value: infer V, ...args: any) => any ? Awaited<V> : never : T,递归剥除 Promise/thenable 包装。工程价值:并发请求的返回类型自动对齐每个异步函数的返回值,新增字段或改变返回结构时类型同步失效;配合 Promise.allSettled 与 PromiseSettledResult 可精确建模"部分失败"场景,避免手写 any 或双重解包(await (await p) 这类错误)。

考察异步类型推导的"解包"语义:Awaited 使 Promise.all 的元组推导保持逐元素精确,工程价值在于并发结果类型自动同步与错误模式的类型表达,回答应说明解包机制与工程收益。

#
★★★

18. DeepReadonly/DeepPartial 在不可变数据建模中的应用

DeepReadonly 与 DeepPartial 如何实现?在不可变数据建模中如何应用?

  • 递归映射类型的实现与终止条件
  • 不可变数据建模中只读层的类型表达
  • 部分更新场景的 DeepPartial 应用

DeepReadonly 递归遍历对象属性并把每一层标为 readonly,实现需处理终止条件:函数类型、Date、RegExp 等不应递归展开,数组/元组需保留结构(映射为 readonly 数组);常见实现用 [T] extends [Primitive] 或检查 object 判断递归边界。应用:状态树、配置对象、API 响应等"初始化后不可变"的数据用 DeepReadonly 建模,防止深层误改;DeepPartial 用于 PATCH 请求体、测试数据构造,只提供需要更新的字段。二者组合(如"基础配置 DeepReadonly + 更新入参 DeepPartial")是前后端契约建模的常见模式。

考察递归工具类型的工程化应用:不可变建模的关键是"把不可变语义递归下沉",而实现细节(函数/类边界、数组处理)决定正确性,回答应给出递归终止策略与典型应用场景。

#
★★★

19. satisfies 与类型断言的差异及团队规范

satisfies 操作符与类型断言(as)有什么区别?团队规范中应如何使用?

  • satisfies 校验形状但保留字面量窄类型
  • as 断言强制收窄或放宽类型
  • 团队规范:优先 satisfies,避免宽类型断言

satisfies 只做"校验"不改变推断结果:表达式必须满足目标类型,但最终类型保持字面量窄推导;as 断言则把表达式的类型强制改为目标类型(可能收窄或放宽),可能掩盖错误并污染后续推断。例如配置对象 const cfg = { mode: 'dev' } satisfies Record<string, string> 校验通过且 mode 仍为 'dev' 字面量;若用 as Record<string, string> 则 mode 被放宽为 string。团队规范上:能校验不断言——用 satisfies 表达"形状约束 + 字面量保留";确需断言时用双重断言与显式注释说明理由,并对 as any 做 code review 拦截。

考察"校验"与"强制转换"的本质差异:satisfies 不改变推断、as 改变推断,回答应通过示例说明二者对字面量类型的不同影响,并给出团队规范建议。

#
★★★

20. Partial、Required、Pick、Omit、Exclude、Extract、ReturnType 的内部实现

Partial、Required、Pick、Omit、Exclude、Extract、ReturnType 这些内置工具类型内部如何实现?

  • 映射类型实现 Partial/Required/Pick/Omit
  • 条件类型实现 Exclude/Extract/ReturnType
  • 内部实现对使用边界的启发

Partial 与 Required 用映射类型加 ?/-? 修饰符实现;Pick 用 { [P in K]: T[P] }(K 受 keyof T 约束)从 T 中挑选键 K;Omit 用 Pick<T, Exclude<keyof T, K>> 组合实现;Exclude 用分布式条件类型 T extends U ? never : T 剔除联合成员;Extract 是 T extends U ? T : never 保留交集;ReturnType 用 T extends (...args: any) => infer R ? R : never 提取返回值。理解内部实现能帮助判断边界:Pick 的键约束、Exclude 只对联合类型有效、ReturnType 对泛型函数与重载的限制等,从而在自定义工具类型时正确组合原语。

考察内置工具类型的"原语拆解":映射类型、分布式条件类型、infer 三原语组合出所有常用工具,回答应逐个给出实现并说明组合关系(Omit = Pick + Exclude),体现对类型编程地基的掌握。

#
★★★

21. composite/declarationMap 与 tsc/swc/esbuild 构建链的协作边界

composite 与 declarationMap 配置在 tsc/swc/esbuild 构建链中有哪些协作边界?

  • composite 对 declaration/rootDir/增量构建的要求
  • declarationMap 与 IDE 跳转体验
  • 非 tsc 转译器(swc/esbuild)不生成声明文件的边界

composite: true 要求同时开启 declaration 并限定 rootDir,其核心价值是配合 Project References 做增量构建与缓存:tsc --build 利用 .tsbuildinfo 跳过未变更文件。declarationMap 生成 .d.ts.map,使 IDE 从声明文件跳回源码,显著改善库消费体验。协作边界:swc/esbuild 等转译器只负责"类型擦除"不做类型检查、也不生成 .d.ts,因此声明文件的产出仍依赖 tsc(或 isolatedDeclarations 下的声明生成流程),工程上常用"esbuild 打包产物 + tsc 生成声明"的分工,并保证 tsc 与转译器对语法(如装饰器、import type)的处理一致。

考察构建链中"谁负责什么"的职责划分:tsc 负责类型检查与声明产出,swc/esbuild 负责快速转译,composite/declarationMap 优化增量与跳转,回答应说明两者边界与协作模式。

#
★★★

22. Awaited 、Promise<Awaited> 在异步函数类型提取与递归解包

Awaited 与 Promise<Awaited> 在异步函数类型提取中如何运用?递归解包的边界是什么?

  • Awaited 的递归解包语义与实现
  • 嵌套 Promise 与 thenable 的处理
  • 异步工具函数(重试、批处理)的类型提取

Awaited 递归解包 Promise 与 thenable 直到非 Promise 类型,Promise<Awaited<T>> 则显式声明"解包后再包装一层"的结果类型,常用于泛型异步函数:async function retry<T>(fn: () => Promise<T>): Promise<Awaited<T>>,其中 Awaited 保证传入值本身也是 Promise 时(如 Promise<Promise<T>>)结果被压平。递归解包边界:解包到非 thenable 即终止,null/undefined 透传,对 thenable 通过 { then(...): any } 结构匹配;避免在类型中混入 thenable 的结构化参数即可保持解包可预测。工程上 Awaited 与 Parameters/ReturnType 组合可从任意函数签名提取并压平异步结果。

考察异步类型"解包-再包装"的精确表达:Awaited 定义解包标准,Promise<Awaited> 定义包装形态,二者组合可用于泛型异步函数的结果类型推导,回答应说明递归终止条件与 thenable 边界。

#
★★★

23. 类型级状态机 与运行时验证(zod/valibot)的协作

类型级状态机是什么?它与 zod/valibot 等运行时验证方案如何协作?

  • 用判别联合表达状态机的状态与迁移
  • 编译期状态约束 vs 运行时数据验证
  • 类型与验证库 schema 的同步策略

类型级状态机用判别联合把状态建模为 { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: T } | { status: 'error'; error: E },配合穷尽性检查(never 检查)保证迁移分支完备,编译器在 switch 后自动收窄出每状态的专属字段。但类型在运行时被擦除,外部输入(API 响应、用户操作)必须经过 zod/valibot 等运行时验证才能安全进入类型空间。协作模式:zod schema 定义运行时契约,其 z.infer<typeof schema> 导出对应类型,类型级状态机消费这些类型并编排迁移;schema 变更时类型自动同步,反之手动断言必须经过验证函数——"运行时验证负责入口,类型级状态机负责内部流转"。

考察"编译期状态建模"与"运行时数据验证"的分工:状态机依赖判别联合与穷尽检查在编译期保证流转正确,zod 在运行时把不可信数据升级为可信类型,二者通过 infer 保持同步,回答应体现边界划分。

#
★★★

24. 类型级事件名生成 与 tsc/swc/esbuild 构建链的协作边界

类型级事件名生成是什么?它与 tsc/swc/esbuild 构建链的协作边界在哪里?

  • 用模板字面量类型生成事件名字符串联合
  • 类型擦除后事件名仍需要运行时字符串
  • 转译器对类型级运算零运行时产物的配合

类型级事件名生成指用模板字面量类型与 as const 从配置推导出事件名联合,如 type Event = \${Prefix}_${Action}``,让 emit/on 的签名自动约束合法事件名,事件总线与消息通信的类型安全由此获得。协作边界:这类类型运算在编译期完成、运行时零产物,swc/esbuild 只需做类型擦除即可正确配合,不涉及生成代码;但运行时实际派发的事件名仍是字符串,须保证配置对象用 as const 保存字面量,且事件名在运行时与类型推导路径一致(通常导出同一份 as const 数组供类型与运行时共用),避免"类型允许但运行时不存在"的漂移。

考察"类型层推导 + 运行时常量共用"的协作:事件名生成的类型部分对转译器透明(零产物),关键是运行时字符串来源与类型推导来源保持同源,回答应说明 as const 同源策略与转译器边界。

#
★★★

25. this 参数类型(ThisParameterType、OmitThisParameter)在库函数的边界适配

ThisParameterType 与 OmitThisParameter 工具类型的作用是什么?在库函数边界适配中有哪些应用?

  • this 参数作为首个参数的类型声明
  • ThisParameterType 提取 this 类型、OmitThisParameter 移除 this 参数
  • 库函数在 bind/call 场景的签名适配

函数可以声明 this 参数(function f(this: Context, x: number)),它不占运行时参数位,仅供类型检查约束调用方的 this 上下文。ThisParameterType 提取函数签名的 this 类型(没有则 unknown),OmitThisParameter 返回去掉 this 参数后的函数类型,常用于把"依赖 this 的回调"转换为纯函数签名。库函数边界适配:包装方法回调(如事件处理器、数组方法回调)时,库内部可能用 call/bind 注入 this,通过 OmitThisParameter 让对外类型不暴露 this 参数;反之,需要保证 this 的适配器用 ThisParameterType 校验调用方上下文,防止严格模式下 this 为 undefined 的类型错误。

考察 this 参数在类型层面的处理:this 参数是类型空间的"隐式第一参数",两个工具类型分别完成提取与剥离,回答应结合严格模式下回调 this 的类型适配说明工程价值。

#
★★★

26. 外部输入以 unknown 接收再通过类型守卫收窄的边界适配

为什么外部输入要以 unknown 接收?类型守卫如何收窄 unknown?边界适配需要注意什么?

  • unknown 作为外部输入的安全接收类型
  • typeof/instanceof/in/自定义 is 守卫的收窄
  • 收窄不全时兜底与运行时验证的配合

外部输入(API 响应、storage 读取、事件数据)类型不可信,用 unknown 接收可强制"先验证后使用",避免 any 把错误推迟到运行时。收窄手段分层次:typeof 处理基本类型(string/number/boolean/object/function/undefined),instanceof 处理类实例(Date、Error、自定义类),in/属性检查处理对象结构,自定义类型守卫(x is T)封装复杂校验逻辑,asserts 函数用于不满足即抛错的场景。边界适配:TS 的结构化收窄对 unknown 只走"基本类型 + 形状判断",复杂结构(数组元素、嵌套对象)需要递归守卫或运行时 schema 验证(zod)把 unknown 升级为可信类型;收窄不完整时编译器提示"类型 unknown",此时不应断言强转,而应补充守卫或显式处理非法分支。

考察"unknown 接收-守卫收窄"的安全通道:unknown 把运行时不可信数据隔离在类型系统的"门口",守卫逐层收窄,回答应覆盖守卫种类、组合方式与未收窄分支的正确处理。

#
★★★

27. 类型守卫(typeof/instanceof/in/自定义 is)与 asserts 函数

typeof、instanceof、in 与自定义 is 类型守卫有什么区别?asserts 函数如何工作?

  • 各内置守卫的适用类型与收窄能力
  • 自定义 is 守卫的签名与实现要求
  • asserts 函数的断言语义与收窄效果

typeof 守卫只对基本类型生效(string/number/boolean/object/function/symbol/bigint/undefined),instanceof 对类实例生效并收窄到具体类,in 运算符用于对象属性存在性判断并可收窄到包含该属性的类型。自定义守卫 function isFoo(x: unknown): x is Foo 用返回值表达收窄,适合封装复杂校验;asserts 函数(asserts x is Foo)在断言失败时抛错,通过后调用方作用域内类型即被收窄,特别适合校验入口参数。工程建议:组合使用——typeof 打底、in 判形状、自定义守卫封装复杂逻辑,入口处用 asserts 校验,保证"验证通过即类型可信"。

考察收窄工具的完整图谱:内置守卫各有适用边界,自定义 is 与 asserts 把验证逻辑与类型收窄绑定,回答应区分"返回值守卫"与"断言守卫"的语义差异与适用场景。

#
★★★

28. 泛型参数、约束与默认类型在 API 设计中的实践

泛型参数、约束(extends)与默认类型在 API 设计中如何实践?

  • 泛型参数声明与类型参数顺序
  • extends 约束与条件类型的关系
  • 默认类型保证可选性与向后兼容

泛型 API 设计三要素:泛型参数声明能力入口,extends 约束参数范围(<T extends Base>),默认类型提供缺省行为(<T = Default>)。实践要点:约束应表达"最小能力需求"而非过度限制——只约束 API 真正依赖的形状,保证宽进严出;默认类型让调用方无感使用,同时保留定制能力;参数顺序上必须默认类型在无默认类型之后;泛型与条件类型结合时注意分布式行为。典型示例:function mapTo<T, U>(fn: (x: T) => U) 用约束与默认平衡灵活性与安全性;库 API 中把"可推断参数"放在前面、"可默认参数"放后面,提升调用体验。

考察泛型 API 设计的工程规范:约束定义能力边界、默认类型定义容错、参数顺序影响调用体验,回答应给出可操作的设计准则与示例。

#
★★★

29. 联合类型、判别联合与交叉类型在业务状态建模中的应用

联合类型、判别联合与交叉类型在业务状态建模中分别如何应用?

  • 联合类型表达互斥状态集合
  • 判别联合用字面量字段区分分支
  • 交叉类型组合能力与冲突风险

联合类型表达"多种互斥形态之一",如请求状态、消息类型、节点种类;判别联合在联合基础上增加字面量判别字段(type: 'a' | 'b'),配合 switch 穷尽检查实现分支安全收窄,是业务状态建模的主力:状态机、表单阶段、权限角色都可用判别联合建模。交叉类型(A & B)组合多个结构,适合"能力叠加"(可拖拽 + 可排序),但同名属性冲突时类型可能变成 never 或语义混乱,需谨慎使用。实践组合:外层判别联合表达状态分支,分支内用交叉或嵌套类型承载专属数据,收窄后用内层结构安全访问字段。

考察三种组合类型的定位差异:联合管互斥、判别联合管"互斥+分支安全"、交叉管叠加,回答应说明各自的适用场景与组合方式,体现建模选型能力。

#
★★★

30. interface 与 type 的声明合并、扩展与计算属性差异

interface 与 type 在声明合并、扩展与计算属性上有哪些差异?

  • interface 支持声明合并,type 不支持
  • 二者扩展对象类型与交集表达
  • 计算属性/映射类型只有 type 支持

interface 与 type 在多数场景可互换,差异集中在三点:其一,声明合并——同名的 interface 自动合并成员(可扩展第三方/全局类型),type 同名即报错;其二,扩展方式——interface 用 extends(可多继承),type 用交叉类型 &,交叉遇到同名冲突属性时会隐式产生 never;其三,能力面——type 支持计算属性、映射类型、条件类型、联合等全部类型操作,interface 只能表达对象/函数/类形状。工程建议:对外部与全局类型扩展、库 API 契约用 interface(合并友好),类型运算、组合类型用 type;ESLint 的 consistent-type-definitions 可统一团队风格。

考察两关键字的能力边界与适用语境:声明合并、扩展语义、类型运算三方面差异决定了选型,回答应给出可操作的工程建议与一致性规范。

#
★★★

31. any、unknown、never、void 的边界与适用场景

any、unknown、never、void 在类型系统中的边界与适用场景是什么?

  • any 关闭类型检查、unknown 强制收窄
  • never 表示不可达/空集、void 表示无返回值
  • 四者误用时的工程后果

any 完全关闭类型检查,赋值与访问任意通过,是"逃生舱"但会污染周边类型推断,应只用于渐进迁移与极端动态场景;unknown 是 any 的安全版——任何值可赋给它,但使用前必须收窄,适合外部输入与泛型兜底;never 表示不可能出现的值(函数永不返回、穷尽分支、联合空集),用于穷尽性检查与条件类型过滤;void 表示函数无有效返回值(显式 return 的表达式会被忽略),区别于 undefined 可被赋值的语义。工程上四者定位清晰:unknown 管入口、never 管穷尽、void 管返回值、any 尽量少用并用 lint 规则限制。

考察四个"边界类型"的语义定位:any 关闭检查、unknown 延迟检查、never 表示空集、void 表示无返回,回答应强调误用后果(any 污染推断、unknown 使用需收窄)与正确场景。

#
★★

32. T extends (...args: any) => infer R ? R : never 在参数与返回值推断的工程价值

形如 T extends (...args: any) => infer R ? R : never 的条件类型在参数与返回值推断上有哪些工程价值?

  • 条件类型约束函数签名后用 infer 提取参数与返回值
  • 与 Parameters/ReturnType 的等价关系
  • 在包装函数、事件系统中的应用

这种写法先约束 T 为函数类型,再用 infer 在 extends 分支提取签名组件:(...args: infer P) => infer R 同时得到参数元组 P 与返回值 R,等价于 Parameters 与 ReturnType 的组合;若 T 不是函数则落入 never 分支,天然过滤非法输入。工程价值:包装函数(节流、防抖、重试、memoize)可用它自动透传原函数参数并推导返回值,事件总线可用它从 handler 签名推出事件载荷类型;结合函数重载取末签名、递归处理可构建更强的签名解析工具。

考察"约束 + 提取"的惯用模式:extends 约束过滤 + infer 结构拆解是函数签名级类型编程的基础写法,回答应说明与内置工具类型的关系及包装场景的价值。

#
★★

33. NonNullable 与显式空值收缩在领域模型的协作

NonNullable 如何工作?它与显式空值收缩在领域模型建模中如何协作?

  • NonNullable 剔除 null 与 undefined
  • 领域模型中可选字段的空值语义
  • 显式收缩(守卫/断言)与类型工具的配合

NonNullable 定义为 T & {}(或条件类型 T extends null | undefined ? never : T),从联合中剔除 null 与 undefined,常用于把"可空输入"转换为"非空处理域"。领域模型协作:模型中用 string | null 表达"字段可空"的真实语义,处理逻辑入口处用非空断言或类型守卫把值收缩到非空域(如过滤数组后 map),再用 NonNullable 声明结果类型,形成"空值在边界处理、内部非空"的建模纪律。注意收缩后的类型检查仍要防运行时 null 穿透(strictNullChecks 下)——类型层面剔除空值不等于运行时剔除,边界校验不可省。

考察空值建模的"类型工具 + 显式收缩"协作:NonNullable 声明非空域,守卫在边界剔除空值,回答应强调类型与运行时的一致性问题。

#
★★

34. Uppercase/Lowercase/Capitalize/Uncapitalize 内置字符串工具类型的边界

Uppercase/Lowercase/Capitalize/Uncapitalize 四个内置字符串工具类型的边界是什么?

  • 四者作用于字面量字符串的类型级大小写转换
  • 只接受字符串类型参数的约束
  • 与模板字面量类型的组合使用

四个类型由编译器 intrinsic 实现,把字符串字面量转换为对应大小写形态:Uppercase 全大写、Lowercase 全小写、Capitalize 首字母大写、Uncapitalize 首字母小写;对宽 string 类型它们返回宽 string(不产生字面量结果)。边界:只接受 string 及字符串字面量,传入非字符串会报错;转换结果是新字面量,可自由嵌套与组合(${Uncapitalize<K>}Id);它们本身不可被用户覆盖实现,属于编译器内置。应用:枚举键风格统一、i18n 键转换、事件名/类名约定生成、模板字面量模式匹配中的形态归一。

考察字符串操作类型的语义与边界:四个类型是"字面量级"的编译期转换,对宽 string 无字面量效果,回答应说明输入约束、输出语义与组合价值。

#
★★

35. TypeScript 结构化类型(structural typing)

TypeScript 的结构化类型(structural typing)是什么?它与名义类型(nominal typing)有何区别?

  • 按形状而非名字判断类型兼容
  • 鸭子类型与赋值兼容规则
  • 结构化类型带来的灵活性代价

TypeScript 是结构化类型系统:两个类型只要形状兼容就互相赋值,不要求声明来源一致——接口、类、类型别名只要结构匹配即可互赋(类还额外要求成员是 public 且兼容)。这与 Java/C# 的名义类型(按名称/继承关系判断)不同,好处是天然适配 JSON、鸭子类型 API,让第三方库互操作顺滑;代价是无法靠类型名区分同构不同义的值(如两个都含 id: string 的类型可互赋),需要 Brand/Nominal 技巧补足。工程理解:结构化是默认行为,跨边界数据(API、事件)天然可用,安全敏感场景用品牌类型显式隔离。

考察类型系统底层模型:结构化按形状兼容、名义按声明兼容,回答应说明结构化带来的灵活性与其在类型安全上的盲区(同构混用)。

#
★★

36. 联合类型(union types)与交叉类型(intersection types)

联合类型与交叉类型分别是什么?在类型建模中如何选择?

  • 联合表达"或"、交叉表达"与"
  • 联合的收窄方式与交叉的属性冲突
  • 与可空类型、判别联合的组合

联合类型 A | B 表达值属于 A 或 B,使用时需收窄(typeof、in、判别字段)才能访问专属成员;交叉类型 A & B 表达同时满足两者,可访问双方成员,但同名属性类型不同时产生 never 冲突。建模选择:互斥形态用联合(请求状态、消息类型),能力叠加用交叉(接口组合、混入类型);联合成员多且需区分时升级为判别联合,交叉注意属性冲突与可读性,复杂场景优先 interface extends 表达继承语义。联合与可空(T | null)组合表达可空建模,与条件类型组合实现分布运算。

考察两种组合类型的本质区别:联合是"或"、交叉是"与",回答应说明收窄方式差异、冲突风险与建模选型准则。

#
★★

37. 字面量类型(literal types)在枚举替代与配置项约束的应用

字面量类型在枚举替代与配置项约束中如何应用?

  • 字符串/数字/布尔字面量类型的语义
  • 联合字面量约束配置项取值
  • 配合 as const 防止字面量拓宽

字面量类型把取值收敛到单一值('success'、42、true),多个字面量组成联合即成为"取值白名单":type Status = 'idle' | 'loading' | 'success'。应用:配置项约束(theme、size、variant 等枚举取值)、事件名、状态码;替代 enum 时可避免运行时对象与编译产物问题。关键陷阱是字面量拓宽:let theme = 'dark' 会推断为 string,需用 const 声明或 as const 保留字面量;对象属性默认拓宽为宽类型,用 as const 整体固定。工程上"联合字面量 + as const + satisfies"组合能在配置表与派生类型间保持同步。

考察字面量类型作为"取值白名单"的建模能力与拓宽陷阱:联合字面量约束取值,as const 阻止拓宽,回答应包含典型应用与防拓宽手法。

#
★★

38. as const 断言在对象字面量与元组类型推断的工程价值

as const 断言在对象字面量与元组类型推断中有哪些工程价值?

  • as const 的深度只读与字面量保留
  • 元组类型推断(宽度固定)
  • 配置表/常量表与派生类型的同源

as const 断言对表达式做"深度只读 + 字面量保留"处理:对象的每个属性变为 readonly 且保持字面量类型,数组推断为固定宽度元组(readonly ['a', 'b'])而非 string[]。工程价值:其一,配置表/常量表用 as const 定义后,通过 typeof 与 keyof 提取精确的键与取值联合,新增配置项自动传播到类型;其二,元组联合(as const 的数组数组)可直接映射为判别联合,省去手工书写;其三,readonly 约束防止运行时误改。与 satisfies 组合可在保留字面量的同时校验形状,是"单一数据源"建模的核心手法。

考察 as const 的双重效果:字面量保留 + 深度只读,回答应说明它与 typeof/keyof/satisfies 组合实现"数据驱动类型"的工程模式。

#
★★

39. 映射类型(mapped types)在 API 响应包装与字段重命名的应用

映射类型在 API 响应包装与字段重命名中有哪些应用?

  • [K in keyof T] 的逐键映射
  • 响应包装(包裹/解包、只读化、可选化)
  • 键重命名与字段裁剪

映射类型遍历源类型的键并生成新对象,典型应用包括:API 响应包装——{ data: T; code: number } 的包裹与解包、整层可选化(Partial-like)、整层只读化;字段重命名——用键重映射 as 把 snake_case 转为 camelCase(as \${...}`配合模板字面量),或按命名约定生成 getter/setter 形态;字段裁剪——按值类型筛选(as never 剔除)与按前缀保留。结合条件类型与 infer 可构建响应解包链:外层{ data: infer D }` 提取内层数据后递归映射,实现"响应类型自动降层"。这些变换让 API 层与业务层的类型适配自动化。

考察映射类型在"对象形状变换"上的通用性:包装、改名、裁剪都是逐键映射 + 键重映射的组合,回答应展示从 API 响应到业务模型的自适应类型转换思路。

#
★★

40. as const 字面量推导 在 Monorepo 与项目引用的协作

as const 字面量推导在 Monorepo 与项目引用(Project References)场景中如何协作?

  • as const 保留字面量以支撑跨包类型推导
  • .tsbuildinfo 增量缓存与类型重算
  • 跨包常量/枚举共享的类型一致性

Monorepo 中常量与配置常放在共享包,as const 保证其字面量类型跨包可见:消费包用 typeof config、keyof 提取精确联合,而非常量被拓宽为 string。与 Project References 协作时,共享包的声明文件(.d.ts)与 .tsbuildinfo 缓存记录类型结果,引用的包通过项目引用加载,需注意:其一,常量变化后引用包的类型检查依赖增量重建,tsc --build 依赖图正确声明(references 列表)才能及时重算;其二,跨包导入在 moduleResolution bundler/node16 下的路径与声明解析要一致,避免"类型漂移"(缓存了旧字面量)。工程上共享包内 as const 常量集中导出,配合 CI 全量 tsc --build 保证缓存失效正确。

考察"字面量类型跨包传播 + 增量构建缓存"的协作:as const 保证类型精度,Project References 决定缓存一致性,回答应说明增量重算与依赖图配置的要点。

#
★★

41. 函数重载与联合类型/条件类型的选择标准,可读性、类型推断精度与维护成本的工程权衡

函数重载与联合类型/条件类型在函数签名设计上如何选择?从可读性、推断精度与维护成本看有哪些权衡?

  • 重载适合参数形态差异明显且数量少的场景
  • 联合参数与条件返回类型减少重载数量
  • 可读性、推断精度与维护成本的权衡

函数重载为"不同参数形态"提供多组签名,推断精确但书写冗长、重载多时维护成本高且实现签名易失配;联合类型参数(x: string | number)一个签名覆盖多形态,但函数体内需收窄且无法表达"参数与返回值的配对关系";条件返回类型(T extends string ? A : B)可在泛型基础上按输入类型映射输出类型,推断精度高但类型表达式复杂。选择标准:参数形态离散且组合少用重载;形态统一用联合参数;需按输入类型变化输出类型且形态规则化用条件类型;大型 API 优先"联合 + 条件类型",将重载限制在用户可感知的核心入口。

考察签名设计的三维权衡:重载精度高成本高、联合简单但精度低、条件类型灵活但复杂,回答应给出按场景的选择准则,体现工程判断。

#
★★

42. 为无类型 JS 库编写声明文件(.d.ts)的流程,模块声明、类型测试与 DefinitelyTyped 发布

为无类型 JS 库编写 .d.ts 声明文件的流程是什么?如何做类型测试并发布到 DefinitelyTyped?

  • 声明文件的模块声明与导出结构
  • 用 tsd/expectTypeOf 做类型测试
  • DefinitelyTyped 的发布流程与规范

编写流程:先分析库的导出面(入口模块、导出函数/类/命名空间、全局变量),再在 declare module 'lib-name'(或按文件结构声明)内写出对应签名,缺类型时用重载、泛型与 any 兜底并注明 TODO;导出形态包括 export functionexport classexport =declare global 扩展等。类型测试用 tsd 或 vitest 的 expectTypeOf:对导出签名做 expectTypeOf(x).toEqualTypeOf(...) 断言,防止声明与实际用法漂移。发布 DefinitelyTyped:fork DefinitelyTyped 仓库(microsoft/DefinitelyTyped)、按规范目录创建 types/xxx/index.d.ts + tsconfig + 测试,通过 CI 的类型检查后自动发布到 npm 的 @types 包;维护者需遵循版本对应、最小依赖与 dtslint 规则。

考察声明文件生产的完整链路:模块声明结构、类型测试保障、开源发布规范,回答应覆盖从本地声明到 DefinitelyTyped 的流程与质量保障手段。

#
★★

43. keyof 与 Lookup Types 在类型查询与约束的现代应用

keyof 与索引访问类型(Lookup Types)在类型查询与约束中有哪些应用?

  • keyof 提取键联合
  • T[K] 索引访问取值类型
  • 键值联动约束与 API 建模

keyof T 提取 T 的全部键组成联合(对象为属性名联合、数组为索引与数组方法键);索引访问 T[K] 取键 K 对应的值类型,K 为联合时结果分布式合并。二者组合的经典应用:getValue<T, K extends keyof T>(obj: T, key: K): T[K] 实现"键与值联动"的类型安全取值;{ [K in keyof T]: T[K] } 的映射依赖 keyof 与索引访问;事件映射 EventMap[K] 按事件名取载荷类型。现代应用还包括:按值类型反查键({ [K in keyof T]: T[K] extends string ? K : never }[keyof T])、表单项与数据模型字段的联动建模。

考察键-值类型联动的核心语法:keyof 取键、T[K] 取值,组合后可构建"字段名与类型对应"的约束体系,回答应给出联动示例与反向查询技巧。

#
★★

44. 模板字面量类型(template literal types)

模板字面量类型的基本能力是什么?它如何提升字符串相关 API 的类型安全?

  • ${} 插值与模式匹配
  • 由字面量推导与分解
  • 事件名、路由、类名等字符串 API 的类型约束

模板字面量类型用反引号 + ${T} 表达字符串模式,T 可以是字面量、联合(自动交叉展开为全部组合)或宽类型(string/number 等);推断时若目标字符串匹配模式,可自动分解出对应片段,${infer P} 捕获变量部分。能力体现:路由模板 /user/${string} 推导参数、事件名 ${Module}:${Action} 枚举组合、CSS 类名前缀约束、字符串拼接的类型化;配合 as const 与 satisfies 可让"字符串约定"成为编译期契约,杜绝手写字符串的拼写漂移,是"字符串即类型"理念的载体。

考察模板字面量类型从"模式声明"到"模式匹配"的完整能力:插值、联合展开、infer 分解三要素构成字符串类型编程基础,回答应说明典型应用。

#
★★

45. never 类型在穷尽性检查(exhaustiveness checking)的工程价值

never 类型在穷尽性检查中有什么工程价值?如何实现?

  • never 表示不可能值/空联合
  • default 分支断言 never 实现穷尽检查
  • 判别联合新增分支时的编译期告警

穷尽性检查利用 never 的"不可赋值"特性:处理判别联合的 switch 后,在 default 分支把剩余值赋给 never 变量(const exhaustive: never = value),若联合存在未处理分支,value 的类型不会收窄为 never 而编译报错;也可用 assertNever(x: never): never 函数,调用即报错。工程价值:状态机、消息处理器、表单阶段等判别联合新增分支时,所有消费点立即编译报错,强制补全处理,把"漏分支"从运行时 bug 提前到编译期。配合 noUnusedLocals 与严格模式,穷尽检查是判别联合建模的收尾保障。

考察 never 的"空集"语义如何转化为编译期守卫:default 分支断言 never 使未处理分支报错,回答应说明实现方式与"新增分支自动告警"的价值。

#
★★

46. 函数重载(function overloads)在多签名 API 的工程取舍

函数重载在多签名 API 设计中有什么工程取舍?

  • 重载签名与实现签名的关系
  • 重载列表的匹配顺序
  • 与联合/条件类型的取舍

函数重载先列若干重载签名,最后写实现签名(含 all 参数并收窄),调用时按声明顺序匹配第一个兼容签名;重载数量多时实现签名需处理全部分支,逻辑复杂且易失配。工程取舍:重载适合"参数形态少且差异明显"的 API(如 parse、create 的多种入参组合),推断精确、文档清晰;形态多或规则化时改用联合参数 + 条件返回类型,减少签名重复;重载的坑包括顺序敏感、无法重载泛型参数的窄化、与接口方法声明的双变差异。规范上"核心入口重载、内部实现单一签名"可控制复杂度。

考察重载机制本身(声明顺序、实现签名)与选型权衡:重载精度高但有顺序与维护成本,回答应说明机制要点与适用边界。

#
★★

47. 类型兼容(assignability)与子类型(subtype)

类型兼容(assignability)与子类型(subtype)是什么关系?判断规则有哪些?

  • 可赋值性与子类型关系的基本概念
  • 结构化兼容、弱类型检查与多余属性检查
  • 函数类型的变体规则

可赋值性指"A 的值能否赋给 B 类型"(B 是 A 的超类型);子类型关系是可赋值性的基础——S 是 T 的子类型则 S 可赋给 T。判断规则:结构化兼容(形状覆盖,源需满足目标全部成员);对象字面量多余属性检查(直接赋值字面量时多余的键报错,变量赋值放行);弱类型检查(全可选目标需至少一个重叠属性);函数类型按"参数逆变(strict 下)+ 返回值协变";数组/元组的可变性放宽。理解规则能解释常见报错(多余属性、联合赋值、回调参数双变)并设计正确的 API 形状。

考察类型系统的兼容性判定模型:结构化子类型 + 附加检查(多余属性、弱类型、变体规则)共同决定可赋值性,回答应覆盖主要规则及其直觉。

#
★★

48. unknown 与 any 在类型安全与渐进迁移的工程差异

unknown 与 any 在类型安全与渐进迁移上有什么工程差异?

  • any 关闭检查与 unknown 强制收窄
  • 渐进迁移中二者的使用策略
  • 污染范围与 lint 治理

any 使"任何操作都合法",类型检查完全失效,且会把宽松性传染给使用方(返回值/参数 any 后下游推断崩溃);unknown 使"任何值可赋、使用需收窄",把检查推迟但保留。渐进迁移场景:把 JS 项目迁移到 TS 时,初期用 any 快速编译通过是常规做法,但应配合"迁移完成即收紧"计划;unknown 适合作为"待验证的边界"(外部输入、序列化数据),配合类型守卫逐步收窄;治理上 ESLint 的 no-explicit-any 与限制使用规则可监控 any 数量。结论:any 是临时逃生舱、unknown 是安全缓冲,目标都是最终收敛为精确类型。

考察两个宽松类型在迁移工程中的定位差异:any 关闭检查代价是污染,unknown 强制收窄代价是使用繁琐,回答应给出迁移策略与治理手段。

#
★★

49. 索引签名(index signatures)与 Record<K, V> 在字典类型的工程应用

索引签名与 Record<K, V> 在字典类型建模上有什么差异与应用?

  • [key: string]: T 索引签名的语义与限制
  • Record<K, V> 对键集合的约束
  • 字典、缓存、映射表建模的选型

索引签名 [key: string]: V 声明"任意字符串键对应 V",常与显式属性并存(显式属性须兼容 V)或用于动态键;Record<K, V> 本质是映射类型 { [P in K]: V },键被约束为 K 联合(字符串/数字/符号字面量),比索引签名更精确。工程选型:键集合已知且有限用 Record(配置表、i18n 映射,键拼写错误编译期暴露);键完全动态用索引签名但注意"属性访问收窄"与 noUncheckedIndexedAccess 下可能返回 undefined 的边界;二者都可配合 as const 与 keyof 实现键值联动。大型字典对象建议显式声明索引类型避免 any 化。

考察字典类型两种表达方式的精度差异:Record 约束键集合、索引签名放宽容键,回答应说明适用场景与 undefined 边界处理。

#
★★

50. const 类型参数(f(x: T))与字面量类型推导的工程取舍

const 类型参数(f<const T>(x: T))是什么?在字面量类型推导上有什么工程取舍?

  • const 类型参数推断为最窄字面量
  • 与 as const 调用侧的对比
  • 对泛型函数推断行为的影响

TS 5.0 引入 const 类型参数:function f<const T>(x: T) 声明后,调用时 T 被推断为参数的最窄字面量类型(对象属性不拓宽、数组为元组),无需调用方写 as const;而普通泛型 T 会把字符串/数字拓宽为宽类型。工程取舍:const 类型参数让"库函数自动保留字面量"成为默认行为,适合接收配置对象、常量表并派生精确类型的 API(如 defineConfig<const T>);代价是推断行为与普通泛型不同,调用方传入变量(已是 string)时按变量类型推断,且大量 const 参数会放大类型实例化成本;与 satisfies 组合可在保留字面量的同时校验形状。

考察 const 类型参数"窄推断"的机制与场景:它把 as const 从调用侧移到声明侧,回答应说明与传统泛型推断的差异及与 satisfies 的配合。

#
★★

51. NoInfer(TS 5.4)在防止默认参数位置泛型推断泄露的工程价值

NoInfer(TS 5.4)是什么?它在防止泛型推断泄露上有什么工程价值?

  • NoInfer 阻止 T 从指定位置参与推断
  • 默认参数/回调位置推断泄露的修复
  • 与条件类型、函数签名的组合

NoInfer 是 TS 5.4 内置工具类型:标记的位置不参与泛型推断,类型仍按 T 校验但推断只从其他位置发生。典型问题:function create<T>(config: T, defaults?: Partial<T>) 中 defaults 的 Partial 会让 T 被 defaults 推断拓宽(推断泄露),用 defaults?: Partial<NoInfer<T>> 后 T 只由 config 决定,defaults 只做校验。工程价值:修复"次要参数污染主推断"的类问题、保证回调与默认值不改变主体类型推导,使泛型 API 推断方向可控、结果更符合直觉。

考察"推断方向控制"的工具:NoInfer 标记禁推位置,回答应说明推断泄露的产生机制与修复示例,体现对泛型推断引擎的理解。

#
★★

52. InstanceType /ThisType 在 Mixin 与多态工厂的应用

InstanceType 与 ThisType 在 Mixin 与多态工厂中如何应用?

  • InstanceType 从构造函数类型取实例类型
  • ThisType 声明对象方法的 this 上下文
  • Mixin 组合与工厂返回类型的推导

InstanceType 用 T extends abstract new (...args: any) => any ? InstanceType<T> : never(内置实现)从构造函数类型提取实例类型,常用于工厂函数返回 InstanceType<typeof Ctor>、实例类型集合与 new 动态构造的类型声明。ThisType 标记对象字面量中方法的 this 为 T,实现"方法体 this 指向外部状态"的建模,常用于对象式配置(methods 里 this 指向组件实例)、Mixin 扩展的上下文注入。Mixin 场景:type Constructor<T = {}> = new (...args: any[]) => T 组合类构造签名,混入后返回 Constructor<Base & Mixin>,配合 InstanceType 推导最终实例形态,实现类型安全的多态工厂。

考察类类型与上下文类型的工具:InstanceType 桥接构造签名与实例、ThisType 调整方法 this,回答应说明在 Mixin/工厂模式中的推导链路。

#
★★

53. Record<string, T> 与 Record<keyof X, T> 在动态键与精确键的取舍

Record<string, T> 与 Record<keyof X, T> 在动态键与精确键场景如何取舍?

  • 宽键(string)与精确键(keyof X)的类型差异
  • 键集合静态已知与动态来源的建模
  • 索引访问与补全体验的差异

Record<string, T> 等价于索引签名 { [k: string]: T },允许任意字符串键,适合键完全动态(用户输入键、运行时拼键);Record<keyof X, T> 把键约束为 X 的键联合,静态已知的键得到编译期校验与 IDE 补全,新增/改键立即报错。取舍:键集合在类型中可枚举(配置表、事件名、字段映射)时用精确键;键确实不可枚举时用宽键并配合运行时校验;宽键的索引访问在 noUncheckedIndexedAccess 下返回 T | undefined,需显式处理;需要"精确键 + 部分动态"时可 Record<keyof X, T> & Record<string, T> 组合,但要注意显式键优先的语义。

考察键粒度对类型安全的影响:精确键带来编译期校验与补全、宽键换来灵活性,回答应说明两种场景的取舍与 undefined 边界。

#
★★

54. 递归类型(Recursive Types)的边界与可维护性

递归类型有什么边界?如何保证其可维护性?

  • 自引用类型与递归结构的表达
  • 深度限制与实例化性能
  • 结构化递归的可读性治理

递归类型指类型定义中引用自身,用于树形结构(JSON、DOM 节点、嵌套评论)、链表与状态嵌套:type Json = string | number | boolean | null | Json[] | { [k: string]: Json }。边界:其一,递归需有"基础分支"终止(联合中的非递归成员),否则类型不可终止;其二,实例化深度受编译器限制(约 50 层默认,该限制在 TS 编译器内部,与 tsconfig 的 maxNodeModuleJsDepth 无关),过深报"Type instantiation is excessively deep";其三,复杂递归拖慢推断。可维护性治理:把递归结构拆成"节点接口 + 递归引用"两个命名类型、避免条件类型中深度递归、用中间类型缓存部分结果、必要时用运行时 schema 承担复杂校验。

考察递归类型的正确性与性能边界:基础分支决定终止、深度限制决定规模,回答应给出结构拆分与性能治理的可维护性手段。

#
★★

55. @types/* 与 TC39 标准化进程的协作

@types/* 包与 TC39 标准化进程之间如何协作?

  • @types 包承载非 TS 语法的类型声明
  • 新 API 提案到标准化的类型演进
  • 版本对应与废弃治理

@types/* 是 DefinitelyTyped 发布的类型包,为 JS 生态(DOM、Node、第三方库)提供类型,其中浏览器/Node 新 API 的类型紧跟规范与实现:TC39/WHATWG 提案进入 Stage 后,types 包先行提供候选类型(标记 @experimental 或随版本演进),标准化后类型收敛为稳定形态;TypeScript 内置的 lib.dom.d.ts 也随浏览器实现更新。协作要点:类型要与实现版本对应(@types/node 版本对应 Node 版本)、提案变化时类型同步修改(breaking 时发布 major)、废弃 API 用 @deprecated 标注;TC39 的 decorators 等语法提案则直接由 TS 语法支持,不需要 @types 承载。

考察类型生态与标准化的联动:新 API 类型先行、随标准演进收敛,回答应说明 @types 的角色、版本对应与废弃治理。

#
★★

56. 类型级递归深度限制与 IDE 性能 在 Monorepo 与项目引用的协作

类型级递归深度限制与 IDE 性能在 Monorepo 与项目引用场景下如何协作?

  • 递归深度限制与实例化成本
  • IDE 的 TS Server 增量检查与缓存
  • Monorepo 中大类型共享时的性能治理

类型级递归(Deep*、Awaited 类)有实例化深度限制(约 50 层)与指数级成本风险,复杂工具类型会拖慢 tsc 与 IDE 的 TS Server(诊断、补全、跳转全部受影响)。Monorepo 与 Project References 协作:共享包的类型被多个消费包引用,TS Server 用 .tsbuildinfo 缓存跨包类型结果,引用关系正确声明可避免重复计算;治理手段包括:避免高深度递归(改写为迭代结构/有限层数)、将复杂类型按需拆到独立文件、在共享包内做类型"薄接口"降低消费端实例化、关闭对全量文件的 watch 诊断、对极端场景用 tsgo/native TS 等性能优化工具;CI 全量 tsc --build 与 IDE 增量检查配置应一致,保证本地与 CI 性能与结果可复现。

考察类型性能在大型仓库中的治理:深度限制是硬边界、实例化成本是软约束,回答应说明 Project References 缓存机制与具体治理手段。

#
★★

57. moduleResolution: bundler 在 SSR/Hydration 边界的类型契约

moduleResolution: bundler 在 SSR/Hydration 边界下如何保证类型契约?

  • bundler 解析模式的语义与前提
  • SSR 与客户端模块解析差异(exports 条件)
  • 同构代码的类型一致性保障

moduleResolution: bundler 假设代码由打包器(Vite/Webpack/esbuild)处理,允许 package.json exports 中的 import/browser/default 条件解析、无扩展名导入、.ts 后缀导入等打包器语义,但要求 module 为 esnext/preserve 且不能用于纯 Node 运行时。SSR/Hydration 边界:同一份同构代码在 Node(服务端)与浏览器(客户端)运行,模块解析依赖 exports 的 node/import 与 browser/import 条件分支,类型层面需保证两个分支导出的类型契约一致(如组件、fetch 封装、环境变量访问),否则出现"类型对但运行时行为分叉"。工程上为同构入口声明统一类型接口,分支实现用条件导出并做类型对齐测试,SSR 构建(tsc node16)与客户端构建(bundler)分别校验同一契约。

考察模块解析模式与双端运行边界的配合:bundler 模式匹配打包器语义,SSR/Hydration 的契约一致性依赖 exports 条件分支的类型对齐,回答应说明配置前提与对齐手段。

#
★★

58. isPlainObject / isUnion / type Guard helpers 在运行时与编译时类型边界协同的工程价值

isPlainObject、isUnion 等类型守卫辅助函数在运行时与编译时类型边界协同上有什么工程价值?

  • 类型守卫把运行时校验映射为类型收窄
  • isPlainObject 等通用守卫的语义
  • 守卫与工具类型、schema 的协同

类型守卫辅助函数(isPlainObject、isArray、isString、isUnion 等)是"运行时判断 + 类型收窄"的载体:function isPlainObject(x: unknown): x is Record<string, unknown> 判断通过后,编译期类型即收窄,之后访问属性不必再断言。工程价值:其一,外部输入(API、storage、事件)经守卫链逐层收窄,从 unknown 安全过渡到精确类型;其二,守卫可组合(isPlainObject && isUnion 判定特定联合形态),配合工具类型表达"运行时验证对应类型形状"的契约;其三,与 zod 等 schema 库协作时,守卫承担"轻量形状校验",schema 承担"重量级契约校验",二者通过推断的精确类型共享。边界:守卫必须与类型声明严格对应,实现与声明不符会导致运行时收窄失真。

考察守卫函数"验证即收窄"的协同价值:运行时判断与编译期类型通过 is 谓词绑定,回答应说明组合使用与"实现与声明一致"的纪律。

#
★★

59. RequiredKeys /OptionalKeys 工具类型在表单契约的应用

RequiredKeys 与 OptionalKeys 工具类型如何实现?在表单契约中有哪些应用?

  • 用映射类型 + 条件类型区分必填/可选键
  • 表单字段必填性建模与校验联动
  • 与 Omit/Pick 组合的表单类型操作

RequiredKeys 可用 { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T] 提取必填键:可选键 Pick<T, K> 会被 {} 空对象覆盖({} extends 成立)而判为 never,必填键保留;OptionalKeys 取反得到可选键集合。表单契约应用:用"必填键集合"驱动校验规则的类型映射(必填键自动关联 required 校验)、构造"仅必填/仅可选"字段类型(Pick<T, RequiredKeys>)、表单部分提交与草稿保存的字段子集推导;字段必填性调整时类型联动更新,避免校验配置与模型漂移。

考察键属性(必填/可选)的提取技巧:利用 {} extends Pick<T, K> 判断可空性,回答应给出实现思路与表单场景的联动建模。

#
★★

60. Uppercase /Lowercase 在 CSS-in-JS 类名与 i18n 键名的工程应用

Uppercase /Lowercase 在 CSS-in-JS 类名与 i18n 键名场景有哪些工程应用?

  • 字符串操作类型的字面量转换
  • CSS-in-JS 类名生成与样式类型推导
  • i18n 键名的规范生成与校验

Uppercase/Lowercase 把字面量转换为对应大小写形态,常用于"统一命名规范"的编译期实施:CSS-in-JS 中由组件名与状态派生类名(\btn-${Lowercase }`),或把主题 token 的键名统一为大写下划线风格(`${Uppercase}``)与样式映射的类型对齐;i18n 场景中按约定生成键名(模块前缀 + 大写动作),用模板字面量类型约束合法键集合,翻译表与类型同源校验,缺键漏键编译期报错。二者常与 as const、satisfies 组合:常量表保留字面量后,转换类型在编译期生成规范形态,运行时字符串由同一来源派生,保证"类型规范的键"与"运行时存在的键"一致。

考察字符串操作类型在命名规范化中的应用:编译期生成规范形态并与运行时同源,回答应展示类名/键名生成与类型约束的组合方式。

#
★★

61. 类型测试(expectTypeOf/tsd)对自定义工具类型的验证

expectTypeOf 与 tsd 如何对自定义工具类型做验证?

  • tsd/expectTypeOf 的类型断言 API
  • 对工具类型正反用例的验证
  • 集成到 CI 与 lint 的流程

tsd 是官方类型测试工具:在 .test-d.ts 文件中对类型做断言,expectTypeOf(x).toEqualTypeOf<Expected>().toMatchTypeOf.toBeAssignableTo 等 API 检查类型关系;vitest 的 expectTypeOf 提供类似能力并集成到单测。对自定义工具类型的验证要点:正向用例(输入已知类型,断言输出与期望相等)、负向用例(断言错误输入产生 never/报错)、边界用例(联合、函数、递归深度);tsd 还能通过 @ts-expect-error 验证"预期编译失败"的位置。工程上把类型测试文件纳入 tsconfig 与 CI:类型断言失败即 CI 失败,防止工具类型重构破坏契约。

考察类型测试的实践方法:类型也是"代码",需要断言与 CI 保障,回答应说明 API 种类、用例组织与集成方式。

#
★★

62. infer const 提案与字面量类型提取

infer const 提案是什么?它与字面量类型提取有什么关系?

  • infer const 对 infer 推断的窄化
  • 与 as const 推断的互补
  • 提案状态与当前替代方案

infer const 是 TypeScript 提案(infer-const),允许在条件类型中 infer const T 或对推断变量附加 const 修饰,使推断结果保留最窄字面量类型(字符串/数字不拓宽、数组为元组),解决"infer 到的类型被拓宽、丢失字面量精度"的问题。当前(TS 5.x)实现 infer 时可用 infer T 后配合约束(如 T extends \${infer P}` ? ...)或调用侧 as const 补偿部分场景,但整体缺乏 infer const 的简洁语义;替代方案包括 const 类型参数(`)在函数层的保留、以及映射类型中的显式窄化。工程上关注提案进展,现有代码用"约束 + 模板字面量 + as const"组合接近目标效果。

考察类型提取的精读化方向:infer 默认拓宽是精度损失的根源,infer const 提案直接解决该问题,回答应说明提案动机与现有替代手段。

#

63. readonly 修饰符在不可变数据与状态管理的工程价值

readonly 修饰符在不可变数据与状态管理中有什么工程价值?

  • readonly 属性的编译期只读语义
  • 与 Readonly 映射的关系
  • 状态管理中的只读建模

readonly 修饰符标记属性"只读",赋值时报错,但只作用于编译期(运行时对象仍可改);可加在属性声明、索引签名与数组/元组(readonly string[]、readonly [a, b])。工程价值:其一,状态管理中 store 的 state 对外暴露 readonly 视图,修改只能通过 action/mutation,把"禁止直接改"写进类型;其二,函数参数用 readonly 数组表达"只读输入",防止被意外修改;其三,配置对象、常量表用 readonly 声明契约;配合 Readonly/DeepReadonly 可整体只读化。注意 readonly 不阻止深层次修改(需要 Deep 版本),且与 mutable 类型互不兼容,接口实现方需保持 readonly 一致性。

考察 readonly 的编译期约束价值:它是"不可变约定"的类型层载体,回答应说明浅层边界、与 Deep 系列的配合及状态管理中的应用。

#

64. 类成员的访问修饰符(public、protected、private)

public、protected、private 三个访问修饰符的语义是什么?工程中如何使用?

  • 三者的可见性与继承访问规则
  • private 与 # 私有字段的差异
  • 访问修饰符的编译期特性

public 默认,任何位置可访问;protected 允许类自身与子类访问(子类实例不能通过外部对象访问父类 protected 成员);private 仅类自身可访问,子类不可。三个修饰符都是编译期约束,产物中不保留(tsc 转译后仍可访问,除非用 # 私有字段或 private field 语法)。工程建议:对外 API 用 public 显式标注,内部实现用 private/protected 表达封装;TS 的 private 与 ES # 私有字段的差异在于 # 是运行时硬约束、不被反射可见,跨库边界建议用 #;子类扩展点用 protected 而非 private,避免破坏继承契约。

考察类封装的三级可见性:编译期修饰符与运行时硬约束的差异是关键,回答应说明各自语义、继承规则与选型建议。

#

65. 抽象类(abstract class)与接口(interface)

抽象类与接口有什么区别?工程中如何选择?

  • 抽象类含实现与抽象成员、接口纯形状
  • 单一继承 vs 多重实现
  • 运行时存在性(抽象类有运行时对象)

抽象类可以有抽象成员(abstract 修饰)与具体实现、构造函数与字段,作为基类被继承(extends),且存在运行时对象(转译后是构造函数),适合"共享实现 + 模板方法"场景;接口只有类型形状(无实现、不可直接实例化),可被任意类型实现且一个类可实现多个接口,编译后零产物。工程选择:需要共享实现/默认行为/受保护成员用抽象类;只约束形状、需要多实现或与联合类型配合用接口;二者可组合(抽象类实现接口 + 补充抽象方法)。类实现接口时需满足全部成员,抽象类留给子类实现的部分用 abstract 声明。

考察"实现共享"与"形状契约"两种抽象层次的差异:抽象类有运行时存在与实现,接口纯编译期形状,回答应说明选型依据。

#

66. Partial、Required、Pick、Omit 在 DTO 转换的工程应用

Partial、Required、Pick、Omit 在 DTO 转换中有哪些工程应用?

  • 四个工具类型的语义
  • DTO 不同阶段(创建/更新/查询)的类型建模
  • 与映射函数、验证库的配合

DTO(数据传输对象)建模常按阶段区分形状:创建用 Omit 去掉服务端生成字段(id、createdAt)再 Partial 化部分字段;更新用 Partial(部分字段可改)或 Pick 限定可更新白名单;查询用 Pick 选择返回字段子集、Omit 剔除敏感字段。四个工具类型组合可表达完整 DTO 矩阵:CreateDTO = Omit<Entity, 'id' | 'createdAt'>UpdateDTO = Partial<CreateDTO>ListQuery = Pick<Entity, 'status' | 'type'>。配合映射函数(entityToDTO 等)与 zod schema,DTO 类型与运行时转换保持同步,接口变更时类型联动更新,减少手工适配错误。

考察工具类型在数据形状转换中的组合建模:按创建/更新/查询划分 DTO 并用 Pick/Omit/Partial 表达,回答应展示典型组合与联动维护方式。

#

67. 类型化事件(typed events)在 DOM 与自定义事件总线的应用

类型化事件在 DOM 事件与自定义事件总线中如何应用?

  • DOM 事件监听的回调参数类型推导
  • 自定义事件总线的键-载荷映射
  • 事件名联合与载荷类型的联动

DOM 事件天然类型化:addEventListener('click', e => ...) 中 e 自动推导为 MouseEvent,配合"事件名到事件类型"的映射(HTMLElementEventMap、WindowEventMap)实现精准回调签名;自定义事件用 CustomEvent 携带载荷,事件名与载荷的关联需自己建模。类型化事件总线用"事件名联合 → 载荷类型"的映射类型实现键值联动:type Events = { 'user:login': User; 'cart:add': Item }emit<K extends keyof Events>(name: K, payload: Events[K])on<K extends keyof Events>(name: K, cb: (p: Events[K]) => void)——事件名拼错或载荷类型不符立即编译报错,事件系统从"字符串自由"升级为"契约制"。

考察事件系统的类型建模:DOM 侧依赖内置事件映射,自定义总线用 keyof + 索引访问实现"名-载荷"联动,回答应展示映射类型实现。

#

68. TypeScript 5.x 新的 const 类型参数在字面量保留的工程价值

TypeScript 5.x 的 const 类型参数在字面量保留上有什么工程价值?

  • const 类型参数的窄推断语义
  • 配置对象与常量表的字面量保留
  • 与 as const、satisfies 的协作

const 类型参数(<const T>,TS 5.0+)声明后,调用处的推断保留最窄字面量:对象属性不拓宽、数组推断为元组,调用方无需写 as const。工程价值:其一,配置/路由/权限表类 API 的"类型由数据推导"更自然——defineConfig<const T>(config: T) 内部用 typeof T 与 keyof 派生精确联合;其二,避免调用方遗漏 as const 导致的类型拓宽事故(普通泛型下字符串会被拓宽为 string);其三,与 satisfies 组合:fn(<const T>(x: T)) 保留字面量后,satisfies 校验形状仍不失真。注意 const 参数与变量入参(已是宽类型)按变量类型推断,以及推断实例化成本。

考察 const 类型参数"声明侧保留字面量"的机制:它把 as const 的负担从调用方转移到声明方,回答应说明语义差异与典型应用。

#

69. satisfies 操作符在保留字面量类型与校验形状的工程应用

satisfies 操作符如何在保留字面量类型的同时校验形状?有哪些工程应用?

  • satisfies 的"校验但不改变推断"语义
  • 与 as 断言的对比
  • 配置表、路由表、i18n 表的应用

expr satisfies T 只做赋值兼容性校验,结果仍保持表达式的原始窄类型(字面量保留),这是与 as T(强制改变推断)的本质区别。工程应用:配置表校验——const config = {...} satisfies Record<string, ConfigItem> 校验形状且保留每项的字面量,配合 keyof/typeof 派生精确联合;路由表校验——satisfies readonly Route[] 保证结构合法且路径字面量不拓宽;i18n 表校验缺 key;组件 props 默认值校验等。团队规范上 satisfies 取代了大量"校验型断言",配合 no-unnecessary-type-assertion lint 可强制使用更安全的写法。

考察"校验与推断分离"的操作符语义:satisfies 是形状校验、as 是类型强制,回答应强调字面量保留与典型应用场景。

#

70. 递归类型(recursive types)在树形结构与 JSON Schema 的应用

递归类型在树形结构与 JSON Schema 建模中如何应用?

  • 树形结构的自引用类型定义
  • JSON 数据的递归联合建模
  • 与 schema 库、遍历函数的配合

树形结构用自引用接口建模:interface TreeNode<T> { value: T; children?: TreeNode<T>[] },递归字段表达嵌套层级,可选 children 表达叶子节点;JSON 数据用递归联合:type Json = string | number | boolean | null | Json[] | { [k: string]: Json },覆盖全部合法 JSON 形态。应用:组件树、目录树、评论楼层的类型建模;JSON Schema 场景中可用递归类型表达 schema 的嵌套(items/properties 递归引用),配合 zod 等运行时 schema(递归 z.lazy)实现"类型 + 校验"双递归;遍历/序列化函数基于递归类型编写,天然获得类型收窄与深度处理提示。

考察递归类型的建模能力:基础分支 + 自引用表达树/嵌套,回答应说明接口递归、联合递归与运行时 schema 的配合。

#

71. 类型索引访问(T[K])与索引访问类型在大型 API 的工程实践

类型索引访问(T[K])在大型 API 的工程实践中有哪些应用?

  • T[K] 的取值语义与键约束
  • 深层属性访问与派生类型
  • API 字段联动与事件载荷提取

T[K] 在 K 为字面量/联合时取出对应值类型,键必须在 keyof T 内(否则报错),是"键值联动"的基础操作。大型 API 实践:从响应类型取指定字段类型(Response['data']['items'])避免重复声明;用联合键一次取多字段类型(Response['data' | 'meta']);按路径逐层提取类型并抽成别名(type Item = ListResponse['data']['items'][number]);配合 keyof 约束泛型参数实现 get<T, K extends keyof T> 安全取值;事件载荷提取(EventMap['click'])、多态字段按判别键取类型等。实践中建议把常用路径提取为具名类型别名,避免深层链式索引降低可读性。

考察索引访问类型在"从结构取类型"上的工程应用:逐层取值、联合取值、键约束,回答应说明典型场景与可读性治理。