想象你是一家连锁餐厅的新任主厨。推开后厨门,墙上挂着一本《后厨运营手册》——火力开多大、食材放哪个柜、出菜怎么摆盘、甜咸怎么平衡,全写在这本手册里。没有它,每位厨师都凭感觉做菜,同一道宫保鸡丁能做出十个版本。TypeScript 项目根目录下,恰好也躺着这样一本“后厨手册”,它叫 tsconfig.json。这本手册决定编译器怎么检查你的代码、把产物输出到哪里、用什么模块方式加载。本文把这本手册一页页拆开,从 compilerOptions 到 strict 全家桶,从 target/module 到 paths 路径别名,再到 include/exclude 的范围控制,最后给出前端、Node、公共库三类项目的推荐配置。

岗位关联

学会 tsconfig 配置,是你在工程化团队里“立得住”的基本功——它能帮你减少线上类型事故、提升代码审查效率,也是前端架构师面试必考的硬通货。

目录
  1. 可视化演示
  2. 初识 tsconfig
  3. compilerOptions 核心
  4. strict 全家族
  5. target 与 module
  6. 模块解析与别名
  7. include 与 exclude
  8. 实战推荐配置
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示TS编译流程、strict严格模式家族开关、target/module编译目标选择及路径别名配置,通过多模式交互理解配置项作用。

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

TypeScript编译流程 TS源码经tsc编译器处理并应用tsconfig配置 编译器选项控制类型检查与输出格式 输出对应target版本的JavaScript代码 tsc --build --target ES6 tsconfig.json是TS项目核心配置文件

初识 tsconfig

想象你是一家连锁餐厅的新任主厨。推开后厨门,墙上挂着一本崭新的《后厨运营手册》——火力开多大、食材放哪个柜、出菜怎么摆盘、甜咸怎么平衡,全在这本手册里。没有它,每位厨师都凭感觉做菜,同一道宫保鸡丁能做出十个版本。TypeScript 项目根目录下,恰好也躺着这样一本“后厨手册”,它叫 tsconfig.json。

tsconfig.json 是 TypeScript 编译器首先读取的配置文件。任何 .ts 文件要被编译成 .js,编译器都会先翻开这本手册,确认类型检查的尺度、输出产物的位置、模块加载的方式。如果你不提供手册,编译器会按一套“出厂默认值”工作——像是厨房没有手册,全凭系统默认做菜。项目一复杂,默认配置很快就会暴露问题。

JS 产物=Compile(TS 源码,tsconfig 配置)\displaystyle \text{JS 产物} = \text{Compile}(\text{TS 源码}, \text{tsconfig 配置})

1. TS 源码:项目中所有参与编译的 .ts 文件,由 include 字段划定范围。

Input={f∣f∈include,f∉exclude}\displaystyle \text{Input} = \{ f \mid f \in \text{include}, f \notin \text{exclude} \}

2. Compile 编译:编译器执行“类型检查 → 语法转换”两个阶段。

Compile(f)=TypeCheck(f)→Transform(f)\displaystyle \text{Compile}(f) = \text{TypeCheck}(f) \to \text{Transform}(f)

3. tsconfig 配置:compilerOptions、strict、include/exclude 等字段组成的总配置对象。

Config={compilerOptions,strict,include,exclude}\displaystyle \text{Config} = \{ \text{compilerOptions}, \text{strict}, \text{include}, \text{exclude} \}

4. JS 产物:每个通过检查的源码文件转换后生成的 JavaScript 文件。

Output={Compile(f)∣f∈Input}\displaystyle \text{Output} = \{ \text{Compile}(f) \mid f \in \text{Input} \}

接手新项目的第一个动作,应该是翻开它的 tsconfig.json——就像新主厨上任先读后厨手册。看完手册,你立刻知道这个项目的类型检查严不严、产物去了哪里、用了哪套模块标准。后面几节,我们把这本手册一页页拆开讲透。

compilerOptions 核心

后厨手册的第二章,是厨房设备参数表:蒸锅放在哪、烤箱预热到几度、调味料按什么顺序下锅。compilerOptions 在 tsconfig.json 里扮演的正是这个角色——它是编译器处理每个文件时遵守的“设备参数”列表。

compilerOptions 是 tsconfig.json 里最大的配置块,里面躺着几十个键值对。最常用的几个包括:outDir 决定产物输出到哪个目录;sourceMap 决定是否生成 .js.map 映射文件,方便调试时从打包后的代码回溯到源码;removeComments 决定是否删掉注释,精简体积;declaration 决定是否为公共库生成 .d.ts 类型声明文件。

output={outDir,sourceMap,removeComments,declaration}\displaystyle \text{output} = \{ \text{outDir}, \text{sourceMap}, \text{removeComments}, \text{declaration} \}

1. outDir 输出目录:指定编译后 JS 文件的存放文件夹。

outDir→dist/ 或 lib/ 等文件夹\displaystyle \text{outDir} \to \text{dist/ 或 lib/ 等文件夹}

2. sourceMap 映射文件:生成 .map 文件,让浏览器调试能定位到 TS 源码。

bundle.js→sourceMapsrc/index.ts\displaystyle \text{bundle.js} \xrightarrow{\text{sourceMap}} \text{src/index.ts}

3. removeComments 注释清理:为 true 时,产物中不保留源码注释。

产物大小=去除注释后的体积\displaystyle \text{产物大小} = \text{去除注释后的体积}

4. declaration 声明文件:为 .ts 生成 .d.ts,让其他项目拿到类型而非仅 JS。

index.ts→index.d.ts+index.js\displaystyle \text{index.ts} \to \text{index.d.ts} + \text{index.js}

这套设备参数,决定了厨房“开火”之后的每一步操作。如果 outDir 配错了,菜做出来放错了仓库;sourceMap 不配,出了 bug 找不到是哪一口锅炒的。理解了这些参数,你就掌控了编译的第一道工序。

strict 全家族

有些餐厅验收菜品靠尝一口,咸了淡了大差不差;米其林餐厅却请来暗访员,每道菜称重、测温、拍照记录。strict 就是 TypeScript 的“米其林暗访制度”——它不是一条规则,而是一整套验收标准。

strict 字段是布尔值。写 "strict": true 时,编译器会一次性激活一组严格检查规则,最常见的四位成员是:noImplicitAny 禁止“说不上来是什么类型”的参数通过;strictNullChecks 不允许把 null/undefined 悄悄塞给期望有值的变量;noUnusedLocals 检查声明了却没人用的变量;strictBindCallApply 校验 Function.prototype.bind/call/apply 的参数是否与函数签名匹配。

strict=noImplicitAny∧strictNullChecks∧noUnusedLocals∧strictBindCallApply∧⋯\displaystyle \text{strict} = \text{noImplicitAny} \land \text{strictNullChecks} \land \text{noUnusedLocals} \land \text{strictBindCallApply} \land \cdots

1. noImplicitAny:函数参数若没有类型标注,且无法推导,直接报错。

function f(x)→noImplicitAny无法推断类型⇒报错\displaystyle \text{function f}(x) \xrightarrow{\text{noImplicitAny}} \text{无法推断类型} \Rightarrow \text{报错}

2. strictNullChecks:可空类型不能直接赋给普通类型变量。

let s: string = null⇒类型不匹配,报错\displaystyle \text{let s: string = null} \Rightarrow \text{类型不匹配,报错}

3. noUnusedLocals:声明了未使用的局部变量,编译直接告警。

const tmp = 1; 且从未使用 tmp⇒报错\displaystyle \text{const tmp = 1; 且从未使用 tmp} \Rightarrow \text{报错}

4. strictBindCallApply:call/apply 传入的参数必须匹配函数签名。

f.call(ctx, 42)若 f 的参数签名是 (s: string)⇒报错\displaystyle \text{f.call(ctx, 42)} \quad \text{若 f 的参数签名是 (s: string)} \Rightarrow \text{报错}

把 strict 打开,一开始会看到满屏报错——就像米其林暗访第一次来,后厨到处都是不合规项。但这些报错恰恰是守卫:它们把运行时才爆炸的问题,提前拦在编译阶段。生产环境的稳定性,多半是靠这扇“严格之门”守出来的。

target 与 module

同一道菜,堂食客人要求装盘精致,外卖客人要求打包结实,中央厨房配送则要真空封存。target 和 module 就是决定“菜要适应哪种食客”的两个旋钮。

target 决定编译器把 TS 语法降级到哪个 ECMAScript 版本:ES5 是 2009 年的老标准,兼容性最好但很多新语法被“拍平”成冗长的降级代码;ES2015 支持 class、箭头函数;ES2020 支持可选链、空值合并。module 决定模块代码用哪种方式“打包上菜”:CommonJS 适合 Node 后端,ESNext 适合 Vite 等打包器,NodeNext 是 Node ESM 时代的推荐写法。

Target 选择=arg⁡max⁡ver{兼容性(ver),新特性(ver)}\displaystyle \text{Target 选择} = \arg\max_{\text{ver}} \{ \text{兼容性}(\text{ver}), \text{新特性}(\text{ver}) \}

1. ES5 与旧环境:老浏览器只认 ES5,target 选 ES5 保证不报语法错误。

const→var,箭头函数→function\displaystyle \text{const} \to \text{var}, \quad \text{箭头函数} \to \text{function}

2. ES2020 新特性:可选链与空值合并是提高代码健壮性的典型特性。

user?.name→ES2020 支持,ES5 需要降级\displaystyle \text{user?.name} \to \text{ES2020 支持,ES5 需要降级}

3. module 系统:决定 import/export 编译成哪种模块加载协议。

import→NodeNext: require/import,ESNext: 保持 import\displaystyle \text{import} \to \text{NodeNext: } \text{require/import}, \quad \text{ESNext: 保持 import}

选错 target,就像把精致堂食交给外卖小哥——菜是好菜,但包装不匹配,送到客人手里全洒了。配置 target/module 前,先想清楚你的“食客”是谁:旧浏览器用户、现代浏览器、还是 Node 服务端。

模块解析与别名

后厨要采买食材,得知道去哪家供应商、走哪条路。模块解析(moduleResolution)就是 TS 找“食材供应商”的方式;paths 则是你在后厨墙上贴的“快捷通道图”:不用记住仓库第几架第几层,写个小名就能直达。

moduleResolution 常见的取值是 node、classic 和 bundler。node 模式模仿 Node.js 的查找逻辑:先找 node_modules,找不到再尝试加 .ts/.tsx/.d.ts 后缀;bundler 模式服务于 Vite/webpack 这类打包工具;classic 是老古董,现在很少用。baseUrl 可以作为基准目录,paths 则定义别名映射,比如把 @/ 映射到 src/。

ResolvedPath=baseUrl+paths[moduleName]+扩展名补全\displaystyle \text{ResolvedPath} = \text{baseUrl} + \text{paths}[\text{moduleName}] + \text{扩展名补全}

1. moduleResolution 策略:决定模块查找的规则,node/bundler 是主流。

\displaystyle \text{查找顺序: 相对路径} \to \text{node_modules} \to \text{后缀尝试}

2. baseUrl 基准目录:告诉编译器“从哪个根目录出发找模块”。

baseUrl=项目根/src\displaystyle \text{baseUrl} = \text{项目根}/src

3. paths 路径别名:配置键值对,把别名映射到真实目录。

"@/*"→["src/*"],import "@" 指向 src/\displaystyle \text{"@/*"} \to \text{["src/*"]}, \quad \text{import "@" 指向 src/}

配好 paths 之后,import 语句会清爽很多。但要注意:这个别名只是“图纸上的捷径”,真正让打包器认路,还要在 vite.config.ts 或 webpack alias 里同步配置。后厨墙上贴了快捷通道,前厅系统也得认这条路,否则外卖订单还是送不到。

include 与 exclude

菜单不是把所有食材都端出去,还得看哪些菜进这本菜单、哪些藏在后厨不对外卖。include 和 exclude 就是 tsconfig 的“菜单控制规则”。

include 告诉编译器“去哪些目录里找 .ts 文件”,支持 glob 通配符,如 src//* 表示 src 下任意层级任意文件;exclude 则把某些目录划掉——最常见的操作是排除 node_modules 和 dist,避免编译依赖库和构建产物。默认情况下,TS 会扫描项目根目录下所有文件,并自动排除 node_modules、bower_components、jspm_packages。

S编译=Sscan∩Sinclude∖Sexclude\displaystyle S_{\text{编译}} = S_{\text{scan}} \cap S_{\text{include}} \setminus S_{\text{exclude}}

1. include 包含规则:用 glob 通配符圈定源码目录。

"src//*"⇒src 目录下全部 .ts 文件\displaystyle \text{"src//*"} \Rightarrow \text{src 目录下全部 }.ts \text{ 文件}

2. exclude 排除规则:从扫描范围中划掉不需要编译的目录。

\displaystyle \text{"node_modules", "dist"} \Rightarrow \text{这些目录不参与编译}

3. 默认行为:没写 include 时默认扫描全部,exclude 默认排除依赖目录。

\displaystyle \text{默认 } S_{\text{scan}} = \text{项目根}, \quad S_{\text{exclude}} = \{\text{node_modules}, \cdots\}

菜单控制看起来简单,却有一个经典陷阱:exclude 只能防止目录被“主动扫描”,如果某个被 include 的文件恰好 import 了一个被 exclude 的模块,编译器依然会把那个模块拉进编译。这就像客人点了菜单上没有的隐藏菜,后厨照样得做——依赖关系才是真正的菜单。

实战推荐配置

快餐店、高端日料、中央厨房,各有各的后厨手册。TypeScript 项目也一样:前端应用、Node 服务端、公共库,需要三套不同的推荐配置。

前端项目(Vite + React/Vue)推荐 "target": "ES2020"、"module": "ESNext"、"moduleResolution": "bundler"——打包器会处理模块,不需要降级成 CommonJS。Node 服务端推荐 "target": "ES2021"、"module": "NodeNext",让产物在 Node ESM 模式直接运行。公共库项目则要开启 "declaration": true 生成 .d.ts,同时把 "strict": true 作为默认要求。跨项目的公共基础配置,可以用 extends 继承一份 tsconfig.base.json,各项目只写差异。

Config项目=Config基础⊕Δ覆盖\displaystyle \text{Config}_{\text{项目}} = \text{Config}_{\text{基础}} \oplus \Delta_{\text{覆盖}}

1. 前端 Vite 配置:target 对准现代浏览器,模块交给打包器。

target=ES2020,module=ESNext,moduleResolution=bundler\displaystyle \text{target} = \text{ES2020}, \quad \text{module} = \text{ESNext}, \quad \text{moduleResolution} = \text{bundler}

2. Node 服务端配置:模块协议对齐 Node 运行时的 ESM 规则。

target=ES2021,module=NodeNext,moduleResolution=node\displaystyle \text{target} = \text{ES2021}, \quad \text{module} = \text{NodeNext}, \quad \text{moduleResolution} = \text{node}

3. 公共库配置:输出类型声明,杜绝消费者拿到“裸 JS”没类型提示。

declaration=true,strict=true\displaystyle \text{declaration} = \text{true}, \quad \text{strict} = \text{true}

4. extends 继承:顶层配置先合并再覆盖,子配置的字段优先级更高。

Final=BaseConfig∪{覆盖项}\displaystyle \text{Final} = \text{BaseConfig} \cup \{ \text{覆盖项} \}

配置没有银弹,只有最合适的业态。选型前先回答三问:我的“食客”是谁——浏览器还是 Node?我的“出菜方式”是什么——打包器还是原生 ESM?我要不要给外部消费者提供“类型菜单”——.d.ts?想清楚这三问,后厨手册自然就写好了。

AI 应用场景

AI 产品前端(对话机器人 Web 端)

阶段:工程化搭建 / 前端基础建设

解决的问题:AI 对话页面交互复杂,接口返回的流式消息字段多且易变,类型混乱导致联调效率低。传统做法是“先跑起来再说”,结果运行时频繁报 Cannot read properties of undefined。配置 "strict": true 和 paths 别名后,AI 消息结构有了明确类型,@/api 简化导入,编译期就能拦截大部分空值访问。

具体案例:一个 LLM 聊天机器人 Web 端,LLM 返回的 choices 数组可能为空。团队通过 strictNullChecks 强制处理空值分支,配合 "target": "ES2020" 使用可选链 data?.choices?.[0],上线后线上空指针错误骤降 90%。

AI Node 后端推理服务

阶段:服务端部署 / API 服务工程化

解决的问题:Node 后端跑 AI 推理服务时,模块加载方式不匹配会导致部署后整体崩溃。某个中间层服务原本 module 误配为 ESNext,产物里保留 import 语法,Node 用 CommonJS 加载时报 Cannot use import statement outside a module。改用 NodeNext 后,import/export 被正确编译,服务稳定运行。

具体案例:一个调用大模型的 Node 中间层服务,将 tsconfig 的 module 与 moduleResolution 统一调整为 NodeNext,同时把 target 设为 ES2021,部署到生产环境后模块加载问题彻底消失。

AI 团队 Monorepo 公共 SDK

阶段:组件库 / 工具链开发

解决的问题:AI 团队内部有模型调用、埋点上报等多个 npm 包,类型声明缺失会导致跨包使用困难。通过 tsconfig.base.json 统一 strict 与 declaration 设置,各业务包 extends 继承后覆盖输出目录,消费者拿到完整类型提示。

具体案例:某 AI 公司的 @ai/sdk 包,用一份基础 tsconfig 统一类型规则,各业务子包只写 extends 和 outDir 差异。跨项目导入时,IDE 自动提示每个模型的入参出参类型,联调效率显著提升。

Python 演示

python95 行
"""mini_tsscan.py模拟 tsconfig 的 include/exclude 文件筛选与 noImplicitAny 严格检查,直接运行即可看到演示输出."""import re # 模拟项目文件树, 等价于磁盘上的 src 目录 (避免依赖真实文件结构)FAKE_FILES = [    "src/index.ts",    "src/api/user.ts",    "src/utils/format.ts",    "src/legacy/old.ts",    "tests/demo.test.ts",    "scripts/build.js",]  def pattern_to_regex(pattern):    """将 tsconfig 的 glob 通配符转换为正则表达式"""    result = ""    i = 0    while i < len(pattern):        if pattern.startswith("**", i):            # ** 匹配任意层级目录            result += "(?:[^/]*/)*"            i += 2            if i < len(pattern) and pattern[i] == "/":                i += 1  # 跳过 ** 后的斜杠        elif pattern[i] == "*":            # 单个 * 匹配一层内任意字符            result += "[^/]*"            i += 1        elif pattern[i] == ".":            result += r"\."            i += 1        else:            result += pattern[i]            i += 1    return "^" + result + "$"  def filter_files(files, include_patterns, exclude_patterns):    """模拟 tsconfig 的 include/exclude 规则"""    selected = []    for f in files:        in_include = any(            re.match(pattern_to_regex(p), f) for p in include_patterns        )        in_exclude = any(            re.match(pattern_to_regex(p), f) for p in exclude_patterns        )        if in_include and not in_exclude:            selected.append(f)    return sorted(selected)  def check_no_implicit_any(code):    """模拟 noImplicitAny: 函数参数缺少类型标注时报错"""    issues = []    for m in re.finditer(r"function\s+(\w+)\(([^)]*)\)", code):        params = m.group(2).split(",")        for p in params:            p = p.strip()            if p and ":" not in p and "=" not in p:                issues.append(f"参数 '{p}' 缺少类型标注, 隐式 any")    return issues  def main():    tsconfig = {        "include": ["src/**/*", "tests/**/*"],        "exclude": ["**/legacy/**"],        "compilerOptions": {"strict": True},    }    print("=== 模拟 tsconfig 文件筛选 ===")    selected = filter_files(        FAKE_FILES, tsconfig["include"], tsconfig["exclude"]    )    for f in selected:        print(f"  [编译] {f}")     print("\n=== 模拟 strict / noImplicitAny 检查 ===")    demo_ts = "function greet(name) { return 'Hello, ' + name; }"    issues = check_no_implicit_any(demo_ts)    if issues:        for issue in issues:            print(f"  [错误] {issue}")    else:        print("  [通过] 所有参数都有类型标注")    print("\n模拟完成: strict 开启后, 无标注参数会被拦下")  if __name__ == "__main__":    main()
目的:这段 Python 脚本模拟了 tsconfig 的 include/exclude 文件筛选,并用正则实现"noImplicitAny"式严格检查,帮你看懂编译配置的工作机制。
  1. FAKE_FILES = [模拟磁盘上的文件树,让演示不依赖真实目录结构
  2. def pattern_to_regex(pattern):把 tsconfig 的 glob 通配符翻译成正则表达式
  3. result += "(?:[^/]*/)*"这段正则表示“零个或多个目录层级”,对应 ** 的语义
  4. if in_include and not in_exclude:模拟 include 命中且不在 exclude 中的文件才进入编译
  5. return sorted(selected)返回最终参与编译的文件列表,排序让输出稳定
  6. for m in re.finditer(r"function\s+(\w+)\(([^)]*)\)", code):扫描代码里的函数定义
  7. if p and ":" not in p and "=" not in p:参数没有类型标注且没有默认值时,视作隐式 any
  8. print(f" [错误] {issue}")模拟编译器在 strict 模式下的报错输出

常见误区

误区我刚建了 tsconfig.json 但不写任何配置,也能编译出 JS,所以 tsconfig 只是个可有可无的装饰文件,不用花心思学。

正解tsconfig 不加配置时,编译器确实会用默认值运行,但默认值等于“没有后厨手册”。项目稍大,类型混乱、产物乱放、模块加载错误会接踵而来。tsconfig 的价值在于把规则说清楚,而不是“有没有这个文件”。它决定了代码审查能不能发现隐患、产物能不能被正确部署。

为何常见:小 demo 里 tsc 直接跑也能出 JS,让人误以为它不重要。等到多目录、多环境、多依赖的大项目,默认配置立刻露馅。

误区`strict: true` 就是打开了“严格模式”这一项配置,跟 `outDir`、`sourceMap` 没什么本质区别。

正解strict 是一个总闸,背后是 noImplicitAny、strictNullChecks、noUnusedLocals、strictBindCallApply、noImplicitReturns 等十余条规则的合集。开了它,等于把整组严格检查同时激活;关掉它,这群“米其林暗访员”全部下班。

为何常见:配置名取得太像单个开关,文档上只说“开启严格模式”,让人们忽略了它是一组规则集合。

误区target 配置越高越好,新语法随便用,反正编译器会处理。

正解target 越高,产物越接近现代 JS,但运行环境不支持就会语法报错。比如老浏览器不认识可选链 ?.,target 设成 ES2020 且产物直接运行时,老浏览器直接白屏。target 应该由“最老的运行环境”决定,而不是由“最新特性”决定。

为何常见:开发者都想用新语法,容易把自己的喜好当成运行环境的基线,忽略了真实用户可能还在用旧浏览器。

误区配 paths 必须配 baseUrl,不然别名就会失效。

正解TS 4.1 之后,paths 可以不依赖 baseUrl,别名相对 tsconfig 所在目录解析。很多老教程和旧代码示例仍然 baseUrl + paths 成对出现,导致“必须配对”的印象。在新建项目里,直接写 paths 就能工作。

为何常见:网上大量旧文章和存量代码都在用 baseUrl + paths 的组合写法,人们按旧示例照抄,形成了惯性认知。

误区`exclude` 里排除的文件,绝对不可能被编译器碰,可以当防火墙用。

正解exclude 只影响“从根目录开始的扫描”。如果一个被 include 的文件 import 了 exclude 里的模块,编译器仍会编译它。exclude 不是安全边界,只是入口范围控制。真正决定编译集合的是“入口文件 + import 依赖图”。

为何常见:把 exclude 当成防火墙,忽略了 import 依赖图会把排除的文件重新拉回来。实际排查的时候才发现,包体积和编译时间并没有因为 exclude 变小。

面试题

Q1. tsconfig.json 是什么?

单选题 · easy

  • A.一个 TypeScript 编译器可执行文件
  • B.TypeScript 项目的编译配置清单← 正确答案
  • C.JavaScript 运行时环境
  • D.代码编辑器的主题文件

答案:B。tsconfig.json 是 TypeScript 的编译配置清单,好比后厨手册,决定编译器如何检查代码、输出产物。A 把配置清单当成了编译器本体,C 是运行环境,D 是编辑器配置,都不沾边。

Q2. 哪个命令能快速生成 tsconfig.json?

单选题 · easy

  • A.tsc --init← 正确答案
  • B.npm start
  • C.node --config
  • D.ts-node --run

答案:A。tsc --init 是官方提供的生成命令,一键生成基础后厨手册。npm start 是启动脚本,node --config 和 ts-node --run 都不是 tsc 的合法命令,无法生成配置文件。

Q3. "strict": true 的底层作用是什么?

单选题 · mid

  • A.开启一组严格类型检查规则← 正确答案
  • B.只开启 noImplicitAny 一条规则
  • C.强制开启 sourceMap 映射文件
  • D.禁止使用 console 进行日志调试

答案:A。strict 是一个总闸,开启后同时激活 noImplicitAny、strictNullChecks、noUnusedLocals 等十余条规则,就像米其林暗访制度同时检查后厨的方方面面。B 只是其中一条子规则,C 和 D 与 strict 无关。

Q4. strictNullChecks 开启后,哪种写法会报错?

单选题 · mid

  • A.let a: string = ""
  • B.let b: string; b = "hello"
  • C.let c: string; c = null← 正确答案
  • D.let d = "hello"

答案:C。strictNullChecks 下,null 不能赋给 string 类型变量,就像空盘子不能当菜端上桌。A 是空字符串但类型正确,B 是正常赋值,D 是类型推断,都不涉及“可空值赋给非空类型”。

Q5. "target": "ES2020" 的含义是?

单选题 · mid

  • A.强制浏览器运行 ES2020 版本
  • B.将语法降级适配 ES2020 标准← 正确答案
  • C.禁止使用 ES2020 之前的新语法
  • D.自动下载 ES2020 的标准库

答案:B。target 决定 TS 语法降级到哪个 ES 版本,ES2020 意味着产物按 ES2020 语法输出。它不能强制浏览器行为,不会禁止旧语法,也不会自动下载任何库。选 target 的本质是在兼容性与新特性之间权衡。

Q6. 给 Node.js 16+ 服务端项目,最推荐哪组配置?

单选题 · mid

  • A.target ES5 配 module ESNext
  • B.target ES2015 配 module CommonJS
  • C.target ES2021 配 module NodeNext← 正确答案
  • D.target ES2020 配 module AMD

答案:C。Node 16+ 支持 ESM,推荐 target ES2021 + module NodeNext,让产物在 Node ESM 模式直接运行。ES5 太老且与 ESNext 搭配不合适;CommonJS 在 ESM 项目里逐步被替代;AMD 是浏览器场景的老方案,与 Node 不匹配。

Q7. paths 配置的核心作用是什么?

单选题 · hard

  • A.提高模块的整体打包编译速度
  • B.为模块名设置路径别名简化导入← 正确答案
  • C.自动修改 npm 包的版本号
  • D.自动下载类型声明文件依赖

答案:B。paths 为模块名设置别名,把 ../../utils/format 这种深路径变成 @/utils/format。它不直接加速编译、不修改 npm 版本、也不下载类型声明。别名的核心价值是代码可读性和重构时的路径可控性。

Q8. 关于 include 和 exclude,正确的说法是?

多选题 · mid

  • A.exclude 可阻止被导入文件编译
  • B.include 的默认值是匹配所有文件← 正确答案
  • C.被引用模块会绕过 exclude 参与编译← 正确答案
  • D.exclude 优先级始终高于 include

答案:BC。include 默认是 /*,匹配所有文件,B 对。被 include 文件 import 的模块即使命中 exclude 也会被编译,C 对。A 错在 exclude 阻止不了依赖图拉取;D 过于绝对,排除规则只是入口扫描的过滤条件。

Q9. 关于 paths 和 baseUrl,正确的说法是?

多选题 · hard

  • A.TS 4.1 之后 paths 可不配 baseUrl← 正确答案
  • B.paths 别名编译时会被真实路径替代← 正确答案
  • C.一个项目只能配置一条路径别名
  • D.paths 会自动创建对应的真实目录

答案:AB。TS 4.1 之后 paths 可以独立于 baseUrl 工作,A 对。别名在编译时被替换为真实路径,B 对。C 错在一个项目可以配置多条别名;D 错在 paths 只是映射关系,不会自动创建目录,目录仍由你自己的代码结构决定。

Q10. 公共库项目的推荐配置包含哪些?

多选题 · hard

  • A.开启 declaration 生成类型声明文件← 正确答案
  • B.关闭 strict 以兼容所有老用户
  • C.开启 sourceMap 方便定位线上问题← 正确答案
  • D.必须把 target 强制设为低版本 ES5

答案:AC。公共库必须输出 .d.ts 类型声明,A 对;开启 sourceMap 有助于消费者在 node_modules 里调试,C 对。关闭 strict 会降低类型质量,是给未来挖坑,B 错;是否用 ES5 取决于目标环境,没有“必须”,D 错。