想象一下,你是一位旅行规划师,正准备带着几百件行李(TypeScript 源码)飞往世界各地。如果完全没有规划,行李会杂乱无章:有的放进随身背包,有的拖运货舱,有的甚至会被安检扣下。而 tsconfig.json 就是那本神奇的“旅行规划手册”——它告诉 TypeScript 编译器这位“打包专家”:代码该编译成什么版本(去哪个国家)、模块该用什么方式加载(走什么通道)、类型检查该多严格(安检级别多高)、产物该怎样优化(行李怎么压缩)。本文将通过一次完整的“代码旅程”,带小白读者彻底看懂 tsconfig compilerOptions 的每一个关键旋钮,从此不再玄学调参。

岗位关联

学会解读 tsconfig compilerOptions,你就掌握了 TypeScript 工程的“方向盘”——无论是搭建新项目、排查编译报错,还是与 AI 协作生成高质量前端代码,都能精准发号施令。

目录
  1. 可视化演示
  2. 配置基础:旅行手册的“开箱指南”
  3. 编译目标:决定“本国话”怎么说
  4. 模块系统:快递包裹的“配送方案”
  5. 类型检查:“安检闸口”的严格程度
  6. 工程优化:大件行李的“提速秘籍”
  7. 配置实践:老旅行家的“百宝箱”
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示tsconfig核心配置:strict严格模式体系、module模块解析与路径别名、project references多项目引用及target编译目标对比,含数据流背景和发光动画。

本节配有交互动效(进入视口后自动加载,约 3KB)。

配置基础:旅行手册的“开箱指南”

旅行前最重要的事是什么?当然是把所有行李清单(tsconfig.json)放到行李箱(项目根目录)最显眼的位置。TypeScript 编译器 tsc 出门前,第一件事就是找这份清单——如果连清单都没有,它会用一脸茫然的表情问你:“你打算带什么走?”。tsconfig.json 就是整个 TypeScript 工程的“主清单”,它通过 compilerOptions 字段记录了一百多个可调旋钮,从“目标语言版本”到“是否压缩注释”,事无巨细。

清单本身有几个“母字段”负责圈定行李范围。include 像是“打包哪些柜子”——告诉编译器把 src 文件夹下的所有 .ts 文件都算入编译;exclude 则是“哪些柜子绝对不碰”,比如 node_modules、dist 这些不该动的区域。files 则精准到逐个点名,像是特别指定的随身物品。还有一个非常有用的 extends 字段——它相当于“参考上一季度的旅行模板”,可以让你的项目配置继承自团队统一的 tsconfig.base.json,避免每个子项目各自为政。

1. include 收纳:include 定义哪些目录或文件参与编译,通常写 ["src"] 表示把 src 目录下所有 TS 文件收纳进编译范围。

f∈includef \in \text{include}

2. exclude 排除:exclude 将某些路径从编译范围中剔除,即使它们曾被 include 收纳,也无法进入编译单元。

f∉excludef \notin \text{exclude}

3. 交集生成:编译单元最终是两者的交集——既包含于 include、又不属于 exclude 的文件的集合。

编译单元={f∣f∈include∧f∉exclude}\text{编译单元} = \{ f \mid f \in \text{include} \wedge f \notin \text{exclude} \}

回到旅行比喻:include 是“打开行李箱放入的衣物”,exclude 是“临走前挑出来的违禁品”。只有同时在“携带清单”里且不在“违禁清单”里的行李,才会真正登机。理解了这个基础,你就能一眼看穿那些“明明写了 include 却还是报文件找不到”的问题——多半是 exclude 把目标误伤了。

text1 行
编译单元 = { f | f ∈ include ∧ f ∉ exclude }

编译目标:决定“本国话”怎么说

行李上了飞机,现在要讨论的是“到了目的地,下飞机说什么语言”。target 选项就是 TS 编译器的“翻译指令”——它决定 TypeScript 这种“高级世界语”最终被翻译成哪个版本的 JavaScript“方言”。如果你的目标环境是老式浏览器(ES5 时代),target: "ES5" 就能把箭头函数、async/await 等新语法全部“降级翻译”成 function() 形式;如果目标环境是 Node.js 22,直接设 "ES2022" 甚至 "ESNext",让代码更贴近原生。

lib 是配套的“目的地词典”。它声明了代码运行时可用的 API 集合:"DOM" 表示浏览器环境提供 document、window 等 API;"ES2021" 提供 Promise.any、String.replaceAll 等内置对象。如果 lib 没选对,编译器会对本来可用的 API 报“找不到名称”,就像你带了本错误的词典去跟当地人交流。

1. 目标版本:target 接受 ES5、ES2015、ES2020 等字符串值,指定输出 JS 的语法版本。

target∈{ES5,ES2015,⋯ }\text{target} \in \{\text{ES5}, \text{ES2015}, \cdots\}

2. 环境词典:lib 指定可用的环境 API 集合,常见的是 "DOM" 与具体 ES 版本的组合。

lib={DOM,ES2021,⋯ }\text{lib} = \{\text{DOM}, \text{ES2021}, \cdots\}

3. 译制映射:编译器将 TS 语法按 target 的规则“降级”,同时按 lib 校验 API 调用,最终产出 JS。

out=transpile(TS,target,lib)\text{out} = \text{transpile}(\text{TS}, \text{target}, \text{lib})

回到旅行比喻:target 是“你的外语水平目标”,lib 是“你随身携带的翻译词典”。去英语国家只带英语词典(target: ES5 + lib: ["DOM"])足够日常交流;非要用法语词典去日本(lib 选错),当地人根本听不懂你的 API 调用。工程上最常见的坑就是 target 定的很新但实际部署环境很老,导致运行时报语法错误——翻译方向搞反了。

text1 行
TS输出 = 译制(TS源, target, lib)

模块系统:快递包裹的“配送方案”

当代码编译完成后,文件之间如何互相引用、加载?这就是模块系统的事。module 选项决定了快递公司的选择:"CommonJS" 像传统的邮政包裹(Node.js 默认的 require 方式);"ESNext" 则像现代化高铁快递(原生 import 语法);"UMD" 像万能物流,浏览器和 Node.js 通吃。选错配送方案,require 用不了 import、import 报错找不到模块——全是家常便饭。

moduleResolution 是“快递员找地址的策略”。"node" 策略模仿 Node.js 的寻址逻辑:先找 node_modules 里的包,再找文件扩展名;"classic" 是老式浏览器时代的寻址方式。另一个重兵器是 paths 字段——它相当于快递地址簿里的“昵称映射”。比如配置 "@utils/*": ["src/utils/*"],那么无论多深的目录,只需写 import x from '@utils/format',编译器就会自动翻到 src/utils/format 去拿货——从此告别 ../../../ 这种天梯式相对路径。

1. 基础前缀:baseUrl 设定所有非相对模块导入的基准目录,简化路径前缀。

base=baseUrl\text{base} = \text{baseUrl}

2. 别名替换:paths 将声明中的昵称映射为真实路径,如 "@utils/*": ["src/utils/*"]。

module↦真实路径\text{module} \mapsto \text{真实路径}

3. 扩展名补全:moduleResolution: "node" 会依序尝试 .ts、.tsx、.d.ts 等扩展名。

path=base+module+ext\text{path} = \text{base} + \text{module} + \text{ext}

在现代前端工程里,路径别名几乎是必备配置。回到“快递”比喻:没有 paths,包裹地址写的是“某省某市某区某街道第 5 栋”,有了别名只需写“甜蜜小屋”这个昵称,快递员(编译器)自动知道送往哪里。baseUrl 则像一个大型物流中心的统一起始坐标——所有包裹从同一个物理位置出发,路径计算就变得简单可预测。

text1 行
解析路径 = baseUrl + 模块名 + 扩展名

类型检查:“安检闸口”的严格程度

机场安检的严格程度决定了飞行安全。TypeScript 的类型检查系统也是如此——strict 就是一个“总闸开关”。当 strict: true 时,它会同时开启 noImplicitAny、strictNullChecks、strictFunctionTypes 等一串子规则,相当于把所有安检通道都调到了最高级别:变量没有明确类型会告警、可能为 null 的值不能直接调用方法、函数参数的类型必须严格匹配。这是 TypeScript 成为“安全型语言”的关键根基。

noImplicitAny 是其中最经典的一条——它禁止“隐式 any”:即变量或参数没有标注类型,编译器也推断不出来时,会默认当成 any(任意类型)放行。strict 模式下,这种“蒙混过关”的行李会被直接拦下,报错提示你补上类型标注。此外还有 strictNullChecks、noUnusedLocals 等,共同织成一张细密的“安全网”。

1. strict 总闸:strict: true 相当于把所有类型检查子项一次性全部开启。

strict⇒⋀ichecki\text{strict} \Rightarrow \bigwedge_{i} \text{check}_i

2. noImplicitAny:禁止未标注类型时自动回退到 any,强制显式类型标注。

∀x, type(x)≠anyimplicit\forall x,\ \text{type}(x) \neq \text{any}_{\text{implicit}}

3. strictNullChecks:null 与 undefined 不再被当作任何类型的合法值。

null∉type(x), ∀x\text{null} \notin \text{type}(x),\ \forall x

回到安检比喻:strict: true 是把安检仪调到最高倍率——连指甲刀(隐式 any)都会被要求出示“类型声明”。很多初学者刚开始觉得严格模式烦人,编译总报错,但正如航空公司宁愿在安检口多花时间也不愿在空中出事故,开启严格模式能拦截掉大量“运行时才爆炸”的隐患。等习惯成自然后,你会发现这些报错其实是编译器在帮你“免费体检”。

text1 行
strict: true ⇔ 安检强度 = 全域最高

工程优化:大件行李的“提速秘籍”

当项目变得庞大,每次重新编译都像重新打包整箱行李,耗时惊人。incremental: true 开启了“增量编译”模式——编译器会把上次编译的信息保存到 .tsbuildinfo 文件,下次只编译改动过的文件和受影响的依赖,就像旅行达人只把新买的衣服塞进已经叠好的行李箱,而不是每次出门都把箱子倒空重来。

removeComments 和 sourceMap 是另外两个实用开关。removeComments: true 在产物中删除所有注释,给快递包裹“瘦身减重”;sourceMap: true 会生成 .map 映射文件,相当于给运行环境一份“行李回溯清单”——浏览器调试时能直接看到 TS 源码而非编译后的 JS。composite: true 配合 references 字段则支撑起“项目引用”这种大型仓库的分治模式:把一个超大项目拆成多个子项目,各自独立编译、缓存复用,像多辆小型货车分头送货,比一辆超长挂车灵活得多。

1. 缓存记录:incremental 生成 .tsbuildinfo,记录文件版本号与依赖关系。

cache=record(文件,版本,依赖)\text{cache} = \text{record}(\text{文件}, \text{版本}, \text{依赖})

2. 差异计算:下次编译仅对缓存中版本号变化的文件及其依赖重新编译。

Δ={f∣version(f)≠cache(f)}\Delta = \{ f \mid \text{version}(f) \neq \text{cache}(f) \}

3. 耗时优化:编译时间正比于增量集大小而非全量大小。

Tinc≈Tfull×∣Δ∣∣All∣T_{\text{inc}} \approx T_{\text{full}} \times \frac{|\Delta|}{|All|}

好比整理行李,增量编译是“只动弄乱的那一层”,全量编译是“把箱子整个倒空重装”。当项目从几百个文件涨到几千个,这个差距从几秒拉大到几十秒。大型 Monorepo(多包仓库)常靠 project references 把几十个子项目按依赖拓扑分组编译——这正是增量哲学在架构层面的延伸。

text2 行
编译耗时 ∝ 未缓存源代码规模增量模式:耗时 ∝ 变更文件规模

配置实践:老旅行家的“百宝箱”

掌握了单项配置,还需要知道如何组合出最优方案。多环境适配是工程实践第一课:常见做法是维护三份配置——tsconfig.base.json(公共基础)、tsconfig.dev.json(开发环境,开启 sourceMap、关闭 minification)、tsconfig.prod.json(生产环境,开启严格优化、移除注释)。三者通过 extends 字段串联成链,就像旅行套装:基础套装是衣物、开发套装是运动鞋、生产套装是正装皮鞋,底层逻辑完全一致,风格随场景切换。

调试体验的关键是 sourceMap: true——它让 Chrome DevTools 能直接显示 TS 源码断点,而不是在编译后的一坨混淆代码里挣扎。对于坑点,最经典的几个:一是 module 和 moduleResolution 不匹配(如 module: "ESNext" 配 moduleResolution: "node" 在某些场景下会解析错乱);二是 target: "ES5" 却用了 lib: ["ES2020"],词典比翻译水平超前;三是 paths 配置后忘记同步 baseUrl,导致别名失效——这些在旅行比喻里就是“地图版本”和“实际地形”对不上号。

1. 环境分层:通过 extends 串联 base/dev/prod 三层配置,共享公共选项,差异化环境选项。

configenv=base⊕overrideenv\text{config}_{env} = \text{base} \oplus \text{override}_{env}

2. 映射对齐:paths 别名必须与 baseUrl 配套,否则相对路径解析基准缺失。

resolve(alias)=baseUrl+alias_target\text{resolve}(\text{alias}) = \text{baseUrl} + \text{alias}\_\text{target}

3. 验证闭环:用 tsc --noEmit 做纯类型检查、sourceMap 验证源码映射,完成配置正确性验证。

valid⇔typecheck∧map(源↔产物)\text{valid} \Leftrightarrow \text{typecheck} \wedge \text{map}(\text{源} \leftrightarrow \text{产物})

老旅行家的经验也并非玄学,而是一套可复用的决策框架。当你在新项目里搭建 TS 配置时,记住这几条心法:基础配置走团队统一模板、开发与生产分离、严格模式必开、调试链路必备。这样即使遇到「AI 生成代码」的现代化工作流,你也能一眼判断 AI 写出的 tsconfig 是否踩了参数搭配的坑——毕竟,旅行规划手册的最终价值,是让你从容应对任何未曾预料的路况。

text1 行
配置成功 = 明确环境 + 匹配引擎 + 验证调试链

AI 应用场景

场景一:AI 辅助创建新前端项目

阶段:需求分析 → 代码生成

解决的问题:以前搭建 TS 项目,开发者需要手动翻文档配 compilerOptions,一个 strict 和 target 的搭配错误可能导致全项目报错。现在 AI 助手(如 GitHub Copilot、Cursor)能根据你提供的“项目类型 + 目标运行环境”自动生成一份合理的 tsconfig 配置,并解释每个选项的用途,就像一位拥有丰富经验的旅行顾问帮你列好行李清单。

具体案例:开发者对 AI 说“帮我写一个 Node.js 20 项目的 tsconfig”,AI 回答 {"target": "ES2022", "module": "NodeNext", "strict": true} 并解释——target 对应 Node 20 支持的 ES2022,module 用 NodeNext 以支持 ESM 与 CJS 共存,strict 确保类型安全。开发者只需微调 include 和 paths 即可。

场景二:大型项目编译性能诊断

阶段:代码分析 → 性能优化

解决的问题:中大型项目常出现“编译一次 30 秒”的糟糕体验。AI 代码分析工具可读取 tsconfig 和项目结构,诊断出 rootDir 设置不当导致重复编译、incremental 未开启导致无缓存、include 范围过大收纳了无关文件等问题,并给出精确的修改建议,相当于让 AI 旅行顾问帮你检查行李是不是装了太多不必要的东西。

具体案例:某团队 Monorepo 项目,AI 分析发现 include: ["/*"] 误将 test 目录收纳进编译,且 incremental 缺失。AI 建议修改 include 限定 src、开启 incremental: true 并配合 tsBuildInfoFile,修改后编译时间从 32s 降至 4s。

场景三:AI 处理类型报错时的“翻译官”

阶段:错误理解 → 修复建议

解决的问题:初学者看到大段 TS 类型报错往往不知所措。AI 编程助手可以充当“报错翻译官”:结合 tsconfig 中的 strict、noImplicitAny 等配置,解释报错根因,并生成符合当前配置约束的修复代码。这就像在异国他乡,AI 导游帮你把路人说的方言翻译成普通话,还告诉你如何调整自己的“行李配置”避免下次再犯。

具体案例:一位开发者遇到 Type 'string | undefined' is not assignable to type 'string',AI 指出这是 strictNullChecks 开启后的预期行为,并建议用可选链或类型守卫处理,同时调整 tsconfig 中相关配置以兼容现有代码风格。

Python 演示

python67 行
# -*- coding: utf-8 -*-"""模拟 tsconfig compilerOptions 的解析与效果评估"""import jsonimport refrom pathlib import Path  class TsConfigAnalyzer:    def __init__(self, config_path):        self.config_path = Path(config_path)        self.raw = json.loads(self.config_path.read_text(encoding="utf-8"))        self.options = self.raw.get("compilerOptions", {})        self.report_list = []     def check_target_lib(self):        target = self.options.get("target", "ES3")        lib = self.options.get("lib", [])        if target == "ES5" and ("ES2020" in lib or "ES2021" in lib):            self.report_list.append("警告: target 为 ES5 但 lib 包含高版本 API,运行时可能报错")        else:            self.report_list.append("目标与 lib 匹配良好")     def check_strict(self):        strict = self.options.get("strict", False)        no_implicit = self.options.get("noImplicitAny", False)        if not strict and not no_implicit:            self.report_list.append("建议: 开启 strict 或 noImplicitAny 以捕获隐式 any")        else:            self.report_list.append("类型安全检查已启用")     def check_incremental(self):        inc = self.options.get("incremental", False)        if not inc:            self.report_list.append("建议: 开启 incremental 以加速大型项目编译")        else:            self.report_list.append("增量编译已启用")     def show_paths(self):        paths = self.options.get("paths", {})        base = self.options.get("baseUrl", ".")        for alias, targets in paths.items():            self.report_list.append(f"别名 {alias} -> {base}/{'/'.join(targets)}")     def generate_report(self):        self.check_target_lib()        self.check_strict()        self.check_incremental()        self.show_paths()        return "\n".join(self.report_list)  if __name__ == "__main__":    demo_config = {        "compilerOptions": {            "target": "ES2020",            "module": "ESNext",            "strict": True,            "incremental": True,            "baseUrl": ".",            "paths": {"@utils/*": ["src/utils/*"]},            "lib": ["ES2020", "DOM"],        }    }    tmp = Path("/tmp/tsconfig_demo.json")    tmp.write_text(json.dumps(demo_config, indent=2), encoding="utf-8")    analyzer = TsConfigAnalyzer(tmp)    print(analyzer.generate_report())
目的:这段 Python 脚本模拟了 TypeScript 编译器对 tsconfig.json 中 compilerOptions 的核心校验逻辑,输出配置健康度报告。
  1. class TsConfigAnalyzer定义了一个配置分析器,相当于一位“配置体检医生”,构造函数接收 tsconfig 路径并解析 JSON。
  2. self.options = self.raw.get("compilerOptions", {})从原始 JSON 中提取 compilerOptions 字段,缺失时回退为空字典,避免 KeyError。
  3. def check_target_lib(self)校验 target 与 lib 的搭配是否合理,模拟编译器对“翻译版本”和“API 词典”的兼容性检查。
  4. def check_strict(self)检查是否启用了严格模式或 noImplicitAny,并给出建议。
  5. def check_incremental(self)检测增量编译是否开启,对大型工程给出性能建议。
  6. def show_paths(self)遍历 paths 别名配置,拼接 baseUrl 展示真实的路径映射关系。
  7. def generate_report(self)汇总所有检查项,生成可读的文本报告。

常见误区

误区我开启了 `strict: true`,为什么有些类型错误还是没报?是不是 TypeScript 检查力不够?

正解strict 是一个“总闸”,它确实带动了 noImplicitAny、strictNullChecks 等核心开关,但并非包含所有检查项。像 noUnusedLocals(未使用变量检查)、noFallthroughCasesInSwitch(switch 分支贯穿检查)等独立选项默认关闭,需要单独开启。类比机场安检:strict 是主通道,但还有若干“附加安检口”需要单独排队。

为何常见:strict 名称暗示了“一切都严格”,初学者容易自然地认为它包含所有检查项,实际上它只是 TypeScript 官方定义的“严格模式全家桶”,并未覆盖全部独立选项。

误区我把 `target` 设为 `"ES5"`,`lib` 设为 `["ES2020", "DOM"]`,编译都通过了,为什么运行时报 `Promise.finally is not a function`?

正解target 控制语法降级,lib 控制 API 类型声明。ES5 的 target 会把语法翻译到 ES5,但 lib: ["ES2020"] 会让编译器认为 Promise.finally 存在——语法上不报错,运行时老环境却没有这个方法。正确的做法是 target 和 lib 保持版本一致,或使用 Polyfill 补全缺失 API。

为何常见:初学者混淆了“语法”和“API”的边界,以为 target 决定了所有行为,实际上两者是独立的两个维度,共同决定产物运行环境。

误区我明明在 `paths` 里配了 `"@utils/*": ["src/utils/*"]`,但是编译还是报“Cannot find module '@/utils/format'”,是不是 TypeScript 的 bug?

正解paths 的左值不会自动生效,还需要同时满足两个前提:一是 baseUrl 必须显式设置(或提供相对路径基准),二是运行时/打包工具(如 webpack、Vite)里的 resolve.alias 也要同步配置。TypeScript 只负责“编译期找到文件”,不负责“运行时模块加载”。

为何常见:paths 配置后,VSCode 的智能提示可能正常(那是 TS 语言服务在工作),但实际编译或打包报错,导致开发者误以为配置无效。

误区我把 `moduleResolution` 设为 `"node"` 后,`import { x } from 'lodash'` 还是报错,是不是 Node 模式解析有问题?

正解moduleResolution: "node" 只是解析策略,遇到没有类型声明的 JS 库时,还需要 allowJs、esModuleInterop 或 @types/lodash 等配套配置。真正的类型来源是 node_modules 里的 .d.ts 声明文件,没有声明文件编译器自然报错。另外,在 "module": "ESNext" 场景下更推荐 "moduleResolution": "bundler"。

为何常见:开发者以为“解析策略”是唯一的救世主,忽略了类型声明文件才是模块类型信息的源头。

误区我用 VSCode 写 TS 代码,不创建 tsconfig.json 也能正常补全和运行,所以配置文件可有可无。

正解编辑器默认用一种宽松的“隐式配置”模式运行 TS,但这种模式下没有开启严格检查、target 默认 ES3、模块解析策略陈旧,产物质量不可控。tsconfig 是工程化的基石,它决定了编译产物、类型安全度、增量缓存等一切工程属性。没有它,代码只能“能跑”,谈不上“可靠、可维护”。

为何常见:语言服务在缺少 tsconfig 时确实会启用默认选项,让简单示例能跑通,但这种“能跑”掩盖了工程风险,让初学者误以为配置无用。

面试题

Q1. tsconfig.json 中 compilerOptions.target 选项的主要作用是什么?

单选题 · easy

  • A.指定 TypeScript 编译器的版本
  • B.决定输出 JavaScript 的语法版本← 正确答案
  • C.控制模块加载的路径解析策略
  • D.设置代码压缩的强度等级

答案:B。target 决定 TS 源码被“翻译”成哪个版本的 JS 方言,如 ES5、ES2020。它不决定编译器版本(那是 tsc 版本的事),不负责模块路径解析(那是 moduleResolution),也不做代码压缩(那是打包工具的职责)。

Q2. 以下哪个配置项用于开启 TypeScript 的全面严格检查?

单选题 · easy

  • A.noImplicitReturns
  • B.strictNullChecks
  • C.strict← 正确答案
  • D.noUnusedLocals

答案:C。strict 是一个总开关,同时开启 strictNullChecks、noImplicitAny 等多个核心子检查项。其他三个虽然是合法选项,但它们都是独立项,严格来说是 strict 的组成部分或平行选项,而不是“全面严格检查”的开关。

Q3. 在 tsconfig.json 中配置 "paths": {"@utils/*": ["src/utils/*"]} 后,还必须要设置哪个选项才能让路径别名生效?

单选题 · mid

  • A.moduleResolution
  • B.baseUrl← 正确答案
  • C.include
  • D.rootDir

答案:B。baseUrl 为 paths 中的相对路径提供基准目录,不设置则别名无法正确解析到目标文件夹。moduleResolution 负责解析策略、include 负责编译范围、rootDir 控制输出目录结构,这些都不是 paths 生效的前提。

Q4. 若项目将运行在 IE11 浏览器上,以下哪组 target 和 lib 配置最为合理?

单选题 · mid

  • A.target: "ES2015", lib: ["ES2020"]
  • B.target: "ES5", lib: ["DOM", "ES5"]← 正确答案
  • C.target: "ES2020", lib: ["ES2020"]
  • D.target: "ES6", lib: ["ES2016"]

答案:B。IE11 仅支持 ES5 语法,故选 target: "ES5",同时 lib 也要配套 ES5 的 API 集合,再补上 DOM 类型。若 target 与 lib 版本不一致(如 A、D),编译能通过但运行时 API 可能缺失;C 配置完全超出 IE11 能力。

Q5. incremental: true 实现编译提速的核心机制是什么?

单选题 · mid

  • A.多线程并行编译所有源文件
  • B.将源码预编译为字节码缓存
  • C.缓存文件信息仅重编译变更部分← 正确答案
  • D.延迟编译直到运行时执行

答案:C。incremental 会生成 .tsbuildinfo 文件记录各文件版本与依赖,下次编译只处理变更文件及其影响面,符合“只动弄乱的那层行李”的增量哲学。它不启用多线程、不预编译字节码、也不把编译推迟到运行时。

Q6. 以下哪些选项与 TypeScript 的“严格类型安全”直接相关?

多选题 · mid

  • A.strict← 正确答案
  • B.noImplicitAny← 正确答案
  • C.removeComments
  • D.strictNullChecks← 正确答案

答案:ABD。strict 是总开关,noImplicitAny 禁止隐式 any,strictNullChecks 禁止 null 被当作正常值。removeComments 是关于产物体积的优化选项,与类型安全检查无关,就像行李压缩袋和机场安检是两码事。

Q7. moduleResolution: "node" 策略下,编译器尝试解析 import x from 'lodash' 时,查找声明文件的正确顺序是?

单选题 · hard

  • A.先查 source 目录下同名文件再查 node_modules
  • B.先找 node_modules/lodash/package.json 中的 types 字段再回退到 index.d.ts← 正确答案
  • C.先查全局 @types 再查 node_modules 内嵌声明
  • D.直接跳过声明文件当作 any 处理

答案:B。Node 模式会先读取模块的 package.json 获取 types 或 typings 字段,找不到时回退到 index.d.ts。全局 @types 是最后兜底机制,而“跳过当 any”是关闭 noImplicitAny 后的默认行为,不是解析策略的一部分。

Q8. 关于 extends 字段,以下说法正确的是?

多选题 · hard

  • A.子配置中的 compilerOptions 会与父配置深度合并← 正确答案
  • B.父子配置中出现同名选项时子配置覆盖父配置← 正确答案
  • C.extends 一次只能继承一个父配置
  • D.配置继承链可以多层嵌套← 正确答案

答案:ABD。extends 可以从父配置继承 compilerOptions,同名选项以子配置为准(覆盖式合并);继承链支持多层嵌套(base → dev → prod 这种链式结构)。但 extends 的值是一个路径字符串,一次只能指定一个父配置,若要继承多个可拆成多层嵌套。

Q9. 以下哪项配置能让浏览器 DevTools 调试时直接定位到 TypeScript 源码行号?

单选题 · easy

  • A.outDir
  • B.sourceMap: true← 正确答案
  • C.declaration: true
  • D.rootDir

答案:B。sourceMap: true 会生成 .map 映射文件,浏览器借此把编译后 JS 的位置映射回 TS 源码。outDir 只管输出目录、declaration 只生成类型声明文件、rootDir 控制源目录结构,都无法提供源码级调试能力。

Q10. 关于 include 与 exclude 的行为,下列哪些描述是正确的?

多选题 · hard

  • A.exclude 可以完全抵消 include 的包含关系← 正确答案
  • B.exclude 只能排除 include 明确包含的文件
  • C.默认情况下 exclude 会自动排除 node_modules 和输出目录← 正确答案
  • D.include 支持 glob 通配符模式匹配文件← 正确答案

答案:ACD。include 支持 src//* 等 glob 模式;默认 exclude 会自动排除 node_modules、dist 等目录;如果一个文件同时被 include 和 exclude 命中,exclude 优先(表现为编译单元取交集)。但“exclude 只能排除 include 包含的文件”是错的——exclude 的排除逻辑独立生效,不受 include 限制。