TC39 与 TypeScript

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

1. Decorators 元数据需求与装饰器标准本身应如何区分,避免继续依赖已废弃的实验语义

Decorators 元数据需求与装饰器标准本身应如何区分?如何避免继续依赖已废弃的实验语义?

  • TC39 Decorators 标准与 legacy experimentalDecorators 的语义差异
  • 元数据(metadata)需求与装饰器语法解耦
  • 从实验语义迁移到标准语义的路径

TC39 标准 Decorators 采用"函数接收 (value, context)"签名、只包装不破坏语义的封装模型,与 TypeScript 旧版 experimentalDecorators 的 (target, key, descriptor) 签名及"声明合并式"行为不兼容;标准中元数据(metadata)作为独立提案演进,未随装饰器定稿——因此"要元数据"不能作为继续使用实验语义的理由。正确做法:区分"装饰器语法"与"元数据需求"两个问题——语法上按 Stage 3 标准写法组织代码(回调式、可参与类型检查),元数据需求用显式注册表或独立元数据提案实现;迁移路径上逐步用编译器插件(如 swc/tsc 的 decorators 选项切换)双编译对比验证行为差异,最后移除实验标志与 legacy 类型声明。

考察对装饰器标准演进的准确理解:标准语义与实验语义不兼容、元数据是独立提案,答案需体现"语法标准化、元数据解耦、迁移验证"的分层策略。

#
★★

2. Iterator Helpers 与 Array 链式方法在无限序列、一次性迭代器和提前终止场景下分别会发生什么

Iterator Helpers 与 Array 链式方法在无限序列、一次性迭代器和提前终止场景下分别会发生什么?

  • 惰性求值 vs 急切求值(数组会耗尽内存)
  • 一次性迭代器(单次消费)与可重放数组的差异
  • 提前终止(break/return)时的迭代器关闭语义

Array 链式方法(map/filter)是急切求值:先构造完整新数组,对无限序列会耗尽内存;Iterator Helpers(Iterator.prototype.map/filter/take 等)是惰性求值:只在消费时逐项计算,天然支持无限序列。一次性差异:数组可反复遍历,迭代器是一次性的——每个 helper 返回新迭代器,共享底层迭代器时消费状态会被破坏,需注意不可重放;提前终止:用 for..of 的 break/return 会调用迭代器 return() 触发关闭(finally 与资源释放),helper 链应正确传播关闭,Array 方法没有该语义。工程上:处理无限/超大数据流用 Iterator Helpers,小数据集合用数组方法。

考察惰性与急切求值的本质区别:无限序列、一次性、提前终止三个场景下 Iterator Helpers 与数组方法行为迥异,答案需突出惰性求值与关闭传播。

#
★★

3. ShadowRealm、Worker、iframe sandbox 与 Node.js vm 的隔离保证为什么不能等同

ShadowRealm、Worker、iframe sandbox 与 Node.js vm 的隔离保证为什么不能等同?

  • 各隔离机制的边界:线程/代理/执行上下文
  • 同步调用与异步通信的差异
  • 安全边界的强弱与逃逸风险

四者的隔离维度不同:Worker 是线程级并行,与主线程异步通信(postMessage),共享浏览器进程但拥有独立事件循环,通信必克隆或转移;ShadowRealm 提供"同一线程内的隔离执行上下文",可同步调用并仅交换原始值,无法共享对象引用,但没有并行能力;iframe sandbox 是文档级边界,通过安全策略(sandbox 标志、origin)隔离脚本与环境,有 DOM 与导航语义;Node.js vm 提供独立 V8 上下文,但 vm 不构成安全边界——内部代码可通过原型链、构造器逃逸访问宿主对象,历史上多次被用于越权。工程结论:按"信任级别"选型——代码不信任且防恶意用 Worker/iframe 加 CSP,需要同步计算用 ShadowRealm,vm 只用于受控沙盒且不能对抗恶意代码。

考察隔离机制的安全模型差异:线程、上下文、文档与 VM 边界强度不同,答案需指出 vm 非安全边界及各机制的通信模型。

#
★★

4. 提案从 Stage 2、2.7、3 到 4 发生状态变化时,编译器插件、polyfill 和 lint 规则应按什么顺序退出

TC39 提案从 Stage 2、2.7、3 到 4 发生状态变化时,编译器插件、polyfill 和 lint 规则应按什么顺序退出?

  • 各 Stage 的含义与稳定性承诺
  • 编译器插件/polyfill/lint 的退出时机差异
  • 退出过程中的双轨运行与验证

提案阶段决定工具链的采用深度:Stage 2 草案语义未冻结,只允许实验性试用,不进入生产依赖;Stage 2.7 是语义稳定草案(语义已达成共识但文本未完成),可开始编译支持但需追踪语义变更;Stage 3 语义冻结、只改文字性细节,编译器可默认启用但应保留切换开关;Stage 4 定稿,polyfill 可移除、插件可默认关闭。退出顺序建议:先移除 polyfill(以特性检测 + 原生优先),再关闭编译器插件(确认目标环境原生支持),最后清理 lint 规则(如 no-deprecated 相关标记)并更新文档;全程用"双轨编译对比 + test262/引擎测试"验证行为一致后再删除代码路径。

考察提案生命周期与工具链治理:各 Stage 的稳定性决定插件、polyfill、lint 的退出顺序,答案需给出"polyfill 先退、插件次之、lint 最后"的节奏与验证方式。

#
★★

5. 从 TypeScript 5.x/6.x 迁移到 7.0 时,怎样用双编译结果比较诊断差异、声明文件和 sourcemap

从 TypeScript 5.x/6.x 迁移到 7.0 时,怎样用双编译结果比较诊断差异、声明文件和 sourcemap?

  • 双编译器并行编译与结果对齐
  • 诊断集合(error/warning)差异的归一化比较
  • 声明文件与 sourcemap 的一致性验证

TS 7.0 原生编译器(Go 实现)与旧版存在行为差异,迁移需双轨验证:同一 tsconfig 分别用旧版 tsc 与 7.0 编译,比较三方面——诊断:把两边的错误/警告按文件+行号+错误码归一化后求差集,新增/消失的诊断逐条人工裁定(如 strict 差异、lib 行为变化);声明文件:对比 .d.ts 输出(可先用 declarationMap 锚定位置),对声明差异做语义化 diff(签名、导出、类型参数);sourcemap:对比 map 内容与"调试时断点/变量"的实测一致性。验证通过标准:诊断差集为空或已裁定、声明文件逐文件等价、sourcemap 抽样断点行为一致,再切换默认编译器并保留回滚开关。

考察编译器迁移的质量保障:诊断、声明、sourcemap 三个维度的双编译对比与归一化差异裁定,答案需给出可执行的验证清单。

#
★★

6. monorepo 如何结合 isolatedDeclarations、项目引用和 declarationMap 缩短增量类型检查的关键路径

monorepo 如何结合 isolatedDeclarations、项目引用和 declarationMap 缩短增量类型检查的关键路径?

  • isolatedDeclarations 对声明生成的约束与并行化
  • 项目引用(references)的依赖图与增量构建
  • declarationMap 在跨项目跳转与缓存中的作用

关键路径 = 最长依赖链上的检查时间,缩短手段:isolatedDeclarations 要求导出 API 显式可推导类型,使声明生成可按文件独立并行(不经全程序推断),把"声明生成"从整程序分析变成单文件操作;项目引用把 monorepo 拆成有向依赖图,每个项目独立编译、输出 .d.ts 作为下游输入,下游只消费声明无需重编译上游源码;declarationMap 让跨项目跳转与编辑器诊断基于映射定位源码,配合增量缓存(.tsbuildinfo)使未变更项目命中缓存。组合效果:上游变更只重编译受影响项目与下游的"检查层",关键路径从全仓编译降为依赖链上的增量范围。

考察 TS 工程化性能:isolatedDeclarations 提供并行声明生成、项目引用做依赖图增量、declarationMap 提升跨项目效率,答案需解释三者如何共同缩短关键路径。

#
★★

7. Temporal 的 Instant、ZonedDateTime、PlainDateTime、Duration 与 Calendar 如何阻止把绝对时间、墙上时间和日历日期混为一谈

Temporal 的 Instant、ZonedDateTime、PlainDateTime、Duration 与 Calendar 如何阻止把绝对时间、墙上时间和日历日期混为一谈?

  • 五类核心类型的时间语义边界
  • 绝对时间、墙上时间、日历日期的混用风险
  • 类型驱动的时间建模与转换规则

Temporal 用类型体系消除歧义:Instant 是绝对时间点(UTC 时刻,与日历无关),ZonedDateTime 是"绝对时间+时区+日历"的完整描述,PlainDateTime 是墙上时间(无时区,仅"某地看到的年月日时分秒"),PlainDate/PlainTime 剥离到日期或时间,Duration 是时间量(可带年月日等日历单位),Calendar 定义日历系统(ISO、伊斯兰历等)。混用风险正是旧 Date 的痛点:绝对时间与墙上时间混淆导致时区偏移错误。类型驱动实践:网络/存储用 Instant(UTC 时间戳),展示用 ZonedDateTime/Intl 按用户时区格式化,日历运算(加一个月)用 PlainDateTime/Duration + Calendar,杜绝"拿 UTC 时间当本地时间加减"。

考察 Temporal 的类型语义:五类类型分别表达绝对时间、墙上时间、日历时间与时间量,答案需说明类型选择如何防止经典时区错误。

#
★★

8. 标准 Decorators 的求值顺序、上下文对象、initializer 与旧版 TypeScript experimentalDecorators 语义有哪些不兼容点

标准 Decorators 的求值顺序、上下文对象、initializer 与旧版 TypeScript experimentalDecorators 语义有哪些不兼容点?

  • 装饰器签名与上下文对象(value, context)的差异
  • 求值顺序:自下而上 vs 自内而外
  • initializer 与 prototype 修饰的语义变化

主要不兼容点:签名——标准装饰器接收 (value, context),context 含 kind、name、addInitializer 等,旧版接收 (target, propertyKey, descriptor) 并允许返回 descriptor 改写;求值顺序——旧版方法装饰器按"声明顺序"自上而下求值、从下到上应用(经典"洋葱"模型),标准版按 kind 分组(类装饰器最后)且 initializer 在实例化时按注册顺序执行;语义——标准版装饰器不能修改方法名、不能直接替换类(返回新类需包装),initializer 取代旧版的"实例初始化逻辑注入"方式;此外标准版在类内作用域严格、不能对参数装饰器做类型元信息假设。迁移时逐条对照改写,用双编译对比验证行为等价。

考察标准与实验装饰器的差异细节:签名、求值顺序、initializer 机制三条主线,答案需具体列出不兼容点与迁移验证方式。

#
★★

9. Iterator Helpers 在同步迭代器上的惰性 map、filter、take、drop、flatMap 与终结操作如何传播关闭和异常

Iterator Helpers 在同步迭代器上的惰性 map、filter、take、drop、flatMap 与终结操作如何传播关闭和异常?

  • helper 链的惰性执行与逐项消费
  • 提前终止时 return() 的关闭传播
  • 异常在链中的传播与资源清理

Iterator Helpers 全部惰性:map/filter/flatMap 不预计算,消费一步算一步;take(n)/drop(n) 控制消费范围,take 在取满后触发底层迭代器的 return() 提前关闭(finally 清理执行)。关闭传播:for..of 的 break/return、或 take 耗尽,会沿链调用各层 return(),直到源头迭代器,保证生成器 finally 运行;未完整消费的迭代器若不关闭会泄漏资源(文件、游标)。异常传播:链中任一回调抛错会沿消费栈向上抛出,同时迭代器被关闭;flatMap 中内层迭代器异常与外层关闭语义一致。工程上:资源型迭代器用 try/finally 或确保 take/break 兜底关闭,并用显式 return() 处理提前终止。

考察迭代器 helper 的协议细节:惰性、take 的提前关闭、return() 传播与异常清理,答案需覆盖关闭传播与资源释放。

#
★★

10. Import Attributes 的静态导入、动态导入及模块缓存键如何处理 JSON 等非 JavaScript 模块

Import Attributes 的静态导入、动态导入及模块缓存键如何处理 JSON 等非 JavaScript 模块?

  • with { type: 'json' } 的静态与动态导入语法
  • 模块缓存键是否包含属性
  • 非 JS 模块的解析与安全约束

Import Attributes 用 with { type: "json" } 声明模块种类:静态导入 import data from './data.json' with { type: 'json' },动态导入 import('./data.json', { with: { type: 'json' } });同一 URL 带不同属性视为不同模块请求,模块缓存键包含 URL 与属性(同 URL 不同 type 各自缓存)。JSON 模块按 JSON 模块语义解析(默认导出解析后的对象),不做脚本执行,降低注入风险;WebAssembly 等其他种类同理。约束:属性不匹配时(声明 json 但内容不是 JSON、或服务端 Content-Type 不符)抛错;旧浏览器需降级为 fetch + JSON.parse。

考察 Import Attributes 的模块语义:静态/动态语法、缓存键含属性、JSON 模块非执行解析与降级,答案需覆盖语法与缓存细节。

import config from './config.json' with { type: 'json' };
const cfg = await import('./config.json', { with: { type: 'json' } });
#
★★

11. TypeScript 7.0 原生编译器迁移中,tsconfig 兼容范围、语言服务协议和旧插件依赖应如何验收

TypeScript 7.0 原生编译器迁移中,tsconfig 兼容范围、语言服务协议和旧插件依赖应如何验收?

  • tsconfig 选项的兼容性矩阵(支持/忽略/报错)
  • LSP/语言服务协议兼容性与编辑器插件生态
  • 旧编译器插件(transform 类)的依赖与替代

验收分三块:tsconfig——建立"选项×行为"兼容矩阵,逐项验证每个配置在 7.0 下的语义(部分选项被废弃、行为收紧或改为默认值),对产生差异的选项(如 module 解析细节、lib 默认值)明确处理方式;语言服务——7.0 原生编译器需与 LSP 对接,验收编辑器内的跳转、补全、重构、诊断与 5.x 一致性,尤其检查第三方语言服务插件(styled-components、GraphQL 等)是否兼容新协议或需替换;旧插件依赖——基于 transform API 的编译插件大多不兼容,需列出依赖清单、评估替代方案(transform 外置、codegen 工具)或验证其在新编译器上的运行结果,全部纳入迁移验收报告并保留回滚。

考察编译器迁移的验收工程:tsconfig 兼容矩阵、LSP 生态、transform 插件三大依赖面,答案需给出逐项验收与回滚策略。

#
★★

12. isolatedDeclarations 为什么要求导出 API 具有可独立推导的显式类型,它如何让声明生成支持并行化

isolatedDeclarations 为什么要求导出 API 具有可独立推导的显式类型?它如何让声明生成支持并行化?

  • 声明生成依赖全程序推断的问题
  • 显式类型标注使单文件独立生成 .d.ts
  • 并行化与增量构建的收益

传统 .d.ts 生成依赖"整程序类型推断":导出函数参数/返回类型可能由实现体推导,必须加载整个依赖图分析才能生成声明,无法按文件并行。isolatedDeclarations 强制导出 API 具有显式、可独立推导的类型(参数、返回、导出变量都要标注),使声明生成变成"单文件语法+类型检查"操作:编译器只需读取该文件与其直接引用的类型声明,即可生成 .d.ts,天然支持按文件并行化(多核、分布式缓存)。收益:大幅缩短大型仓库的声明生成与增量检查时间,也让"单文件转译"(如 esbuild/tsx 生态)能直接产出声明。代价是样板代码增多,需配合 lint 规则强制标注。

考察 isolatedDeclarations 的设计动机:显式类型解除全程序依赖、单文件生成声明、并行化收益,答案需说明机制与代价。

#
★★

13. Pattern Matching 提案尚未定稿时,哪些语法和穷尽性假设不应进入生产代码或公共类型声明

Pattern Matching 提案尚未定稿时,哪些语法和穷尽性假设不应进入生产代码或公共类型声明?

  • 未定稿提案的语法不稳定风险
  • 穷尽性检查(exhaustiveness)的语义假设
  • 稳定代码与实验探索的隔离

未定稿提案(如 TC39 Pattern Matching)的风险:语法形式可能大幅调整(match 表达式、模式语法、守卫写法),类型层面的穷尽性检查、可判别联合的编译保证也未冻结——一旦"编译器保证穷尽"的假设落空,运行时可能产生未处理分支。不应进入生产/公共声明的内容:依赖具体语法的类型声明(如 pattern 类型、匹配专用语法)、把"当前草案的穷尽性语义"写进公共 API 契约、以及基于草案行为编写的库(其消费者会绑定不稳定语义)。安全做法:实验代码隔离在 feature flag 或独立包内,公共 API 用标准 TS 类型表达(可判别联合 + switch),把提案当作"方向参考"而非契约,待 Stage 3/4 后再迁移。

考察对未定稿标准的工程纪律:语法与穷尽性语义均未冻结,答案需说明隔离实验与用标准类型表达契约的策略。

#
★★

14. 如何依据 TC39 阶段、规范文本、test262、引擎实现和 Baseline 状态维护提案采用清单

如何依据 TC39 阶段、规范文本、test262、引擎实现和 Baseline 状态维护提案采用清单?

  • 五类信号的含义与优先级
  • 采用清单的数据字段与更新机制
  • 从"采纳"到"移除 polyfill"的完整流程

维护采用清单需综合五类信号:TC39 阶段(Stage 3 语义冻结前不投入生产)、规范文本(确认语义与边缘行为,避免文档与实现脱节)、test262(提案配套测试用例的完备度,是行为验证的权威来源)、引擎实现(V8/JSC/SpiderMonkey 的实现状态与 flag)、Baseline 状态(四浏览器发布与 30 个月窗口,决定能否移除 polyfill)。清单每条记录:特性、Stage、test262 覆盖、各引擎版本与 flag、Baseline 状态、polyfill 策略与验证链接;更新由 CI 定时抓取(TC39 进程页、引擎 status 页、caniuse/BCD 数据)生成 diff 并人工裁定。决策规则:Stage ≥ 3 + test262 完备 + 引擎实现可用才允许"实验启用",Baseline Widely 才移除 polyfill。

考察标准采用治理:五类信号各有权威性侧重,答案需给出字段设计、自动更新与"采纳→移除 polyfill"的决策门槛。

#
★★

15. Temporal polyfill 与原生实现并存时,序列化、时区数据库版本和跨 Realm 品牌检查怎样保持一致

Temporal polyfill 与原生实现并存时,序列化、时区数据库版本和跨 Realm 品牌检查怎样保持一致?

  • polyfill 与原生对象的并存与品牌检查(Temporal.Instant.from 等)
  • 时区数据库(tzdata)版本差异导致的结果漂移
  • 序列化格式(ISO 字符串)的互通与降级

并存场景(部分用户有原生、部分走 polyfill):品牌检查——polyfill 应复用标准"构造器内检"路径,让 Temporal.Instant.from(polyfillValue) 在原生环境也能解析(按规范要求仅接受 Temporal 品牌对象,因此 polyfill 对象需经字符串或标准转换桥接);时区数据库——原生实现内置的 tzdata 版本与 polyfill 捆绑版本不同,同一时区同一时刻的历史偏移(如 DST 规则变更)可能算出不同结果,应固定策略:以 polyfill 版本为准做测试快照,或统一升级最低浏览器要求;序列化——统一用 ISO 8601 扩展字符串(带偏移)作为交换格式,避免直接传递对象,使 polyfill/原生互通;跨 Realm(iframe)传递时用字符串或规范化的 valueOf 检查。

考察 polyfill 与原生并存的一致性工程:品牌检查桥接、tzdata 版本差异、ISO 字符串交换,答案需给出可操作的互通协议。

#

16. 原生编译器性能提升后,哪些瓶颈仍可能来自模块解析、声明膨胀、编辑器插件或文件系统

原生编译器性能提升后,哪些瓶颈仍可能来自模块解析、声明膨胀、编辑器插件或文件系统?

  • 编译器之外的类型检查瓶颈来源
  • 模块解析(node_modules 遍历、路径解析)与声明膨胀
  • 编辑器插件与文件系统 IO 的耗时占比

即使原生编译器把"类型计算"提速,端到端耗时仍受四类瓶颈支配:模块解析——每文件按 resolver 规则遍历 node_modules、尝试多种扩展名与条件导出,慢在 IO 与目录扫描而非计算,可用 bundler-style resolver、缓存解析结果与显式 paths 缓解;声明膨胀——依赖的 .d.ts 巨大(如大型 UI 库),即使源码未变也要反复加载解析,需用 skipLibCheck、按需依赖与 declaration 优化;编辑器插件——语言服务里第三方插件(transform 型)逐文件执行拖慢补全与诊断,应审计插件开销、禁用无关插件;文件系统——冷缓存时的目录枚举与文件读取(Windows 尤其明显),可用 watch 缓存、增量 .tsbuildinfo 与内存文件系统方案。

考察类型检查性能的全局视图:计算提速后 IO 与生态瓶颈凸显,答案需覆盖解析、声明、插件、文件系统四类并给出对策。

#

17. 第三方类型包未适配严格索引访问时,应用代码、适配声明与上游修复的责任边界如何划分

第三方类型包未适配严格索引访问(noUncheckedIndexedAccess)时,应用代码、适配声明与上游修复的责任边界如何划分?

  • noUncheckedIndexedAccess 引入的 undefined 联合语义
  • 应用侧收缩(narrowing)与适配声明的职责
  • 上游修复的推动与本地覆盖的取舍

noUncheckedIndexedAccess 开启后,索引访问(arr[i]、obj[key])返回类型含 undefined,未适配的第三方类型包会让调用处报错。责任分层:应用代码负责在调用点做类型收缩(判空、断言、非空运算符或解构默认值),不能为了"通过编译"批量加非空断言掩盖逻辑风险;适配声明(module augmentation 或本地 .d.ts 覆盖)用于修正第三方包确实错误的类型形状,但应最小化、注释来源并关联上游 issue;上游修复是最终目标——提 PR 让包自身类型适配严格模式。取舍原则:应用层收缩优先(不污染依赖类型),适配声明只覆盖"包类型错误"而非"应用逻辑缺陷",避免把本地适配声明当长期债而不推动上游。

考察严格模式下的类型责任治理:收缩属应用、适配声明针对包错误、上游修复兜底,答案需明确三层边界与取舍。

#

18. 发布 npm 类型包时怎样验证不同 TypeScript 版本对 exports、typesVersions 与生成声明的解析一致性

发布 npm 类型包时,怎样验证不同 TypeScript 版本对 exports、typesVersions 与生成声明的解析一致性?

  • exports 的 types 条件与 typesVersions 的解析规则
  • 多 TS 版本矩阵下的声明解析验证
  • 发布前检查清单(tsc 自检、消费者模拟、声明测试)

验证要点:exports 字段的 types 条件(或顶级 types)与 typesVersions 的版本映射(旧 TS 用 typesVersions、新 TS 走 exports,二者需保持一致否则解析不一致);生成声明文件必须真实存在且与源码结构对应。验证方法:本地模拟消费者——用多个 TS 版本(如 4.9/5.x/6.x/7.x)分别安装包并 tsc --traceResolution 检查实际解析到的声明文件路径与内容;对声明文件做类型级测试(dtslint/tsd)覆盖公共 API;发布前用 publint/arethetypeswrong 自动审计 exports 与 types 配置。一致性标准:任何受支持 TS 版本解析到的 .d.ts 语义等价,且无"声明存在但解析失败"(如 types 指向未打包路径)。

考察类型包发布的工程验证:exports 与 typesVersions 双轨一致、多版本解析矩阵、自动审计工具,答案需给出可执行的验证流程。