tsconfig.json常用配置详解
1. 先看顶层配置:决定“项目范围”
extends
表示“继承另一个配置文件”。子配置会覆盖父配置,所以它很适合抽一个 tsconfig.base.json 放公共规则,再给前端、后端、测试分别写自己的 tsconfig.*.json。官方还说明,files、include、exclude 会直接覆盖基配置里的同名项。
include
指定要纳入编译的文件或 glob 模式,比如 src/**/*、tests/**/*。如果没有写 files,默认会匹配 **/*。它的目的就是告诉 TS 扫描哪些源码目录。
exclude
表示在 include 的基础上排除哪些路径。要注意它不是绝对屏蔽:如果某个被排除文件又被别的文件 import 到,或者通过 files / types / reference 引入,它仍然可能进入编译。它的目的主要是缩小扫描范围,常见如排除 node_modules、构建产物、临时脚本。
files
显式列出要编译的文件白名单。适合文件特别少的小项目;文件多时通常用 include 更方便。
references
项目引用。适合 monorepo 或大型仓库,把大项目拆成多个小 TS 项目后互相引用。官方说明它可以提升构建和编辑器交互速度,也能强化模块边界。
2. 环境相关:决定“代码跑在哪、按什么 JS 能力编译”
target
指定输出的 JavaScript 目标版本,比如 ES2019、ES2020、ES2022、ESNext。它的核心作用是:决定 TS 要不要把新语法降级。例如目标是 ES5 时,箭头函数会被转成普通函数;而且 target 还会影响默认的 lib。
lib
指定要加载哪些内置类型库,比如 ES2022、DOM。它决定你在类型层面能不能直接使用 document、Map、Promise 等全局 API。目的就是把“运行环境提供了哪些 API”告诉 TypeScript。
jsx
控制 .tsx 中 JSX 最终如何输出。它只影响从 .tsx 来的 JS 输出,React / Preact / 其他 JSX 框架都会用到。目的就是让 TS 知道 JSX 应该如何被保留或转换。
3. 模块系统相关:决定“import/export 怎么理解”
module
指定模块系统,比如 commonjs、esnext、nodenext、preserve。官方现在的建议很明确:现代 Node 项目通常更可能需要 nodenext,而会交给 bundler 打包的项目通常更适合 preserve 或 esnext。它的目的不是单纯决定输出长什么样,还会影响 TS 的类型检查和模块解析行为。
moduleResolution
指定模块解析策略,比如 bundler、node16、nodenext。如果你是 Vite / Webpack / Rspack / esbuild 一类打包项目,常见会配合 bundler 思路;如果是现代 Node,则常用 node16 / nodenext。它的目的就是让 TS 以更接近真实运行时的方式找模块。
baseUrl
设置非相对导入的基础目录。比如写了 baseUrl: "." 后,可以从项目根开始解析某些裸路径导入。官方还说明,这种解析优先级会高于 node_modules 查找。它的目的就是支持更短、更整洁的导入路径。
paths
给导入路径做映射,比如把 @/* 指向 src/*。它会相对 baseUrl(如果设了)或 tsconfig 自身目录去解析。它的目的就是做别名映射,让代码里少写很长的相对路径。
esModuleInterop
常见于 Node 生态和历史 CommonJS 包混用场景。开启后,TS 会通过辅助函数改善 CommonJS 与 ES Module 的互操作,让默认导入等写法更顺手。它的目的就是降低 import 第三方老包时的兼容问题。
types
默认情况下,所有可见的 @types 包都会参与编译;如果你显式写了 types,则只包含列出的那些类型包。它的目的就是控制全局类型污染,例如只保留 node、jest、vite/client。
4. 严格检查相关:决定“类型检查有多严格”
strict
最重要的总开关之一。官方说明它会启用一整组严格检查规则,从而提供更强的正确性保证;但未来 TS 升级时,strict 也可能带来新的报错。实践上,新项目通常建议直接开。
noImplicitAny
禁止那些 TS 推断不出来、最后偷偷变成 any 的地方。目的就是避免类型系统失效却不自知。
strictNullChecks
开启后,null / undefined 会被认真区分;像 find() 这种可能拿不到值的地方,TS 会要求你先判断再访问。目的就是减少空值运行时错误。
noUnusedLocals
本地变量声明了但没用就报错。目的就是清理死代码、减少维护噪音。
noUnusedParameters
函数参数没用就报错;不过以下划线 _ 开头的参数会被豁免。目的就是提醒你接口是否写多了、实现是否遗漏了逻辑。
实际开发里,还很常见一起打开 noImplicitReturns、noFallthroughCasesInSwitch、useUnknownInCatchVariables、exactOptionalPropertyTypes、noUncheckedIndexedAccess 这类更细的严格规则,用来进一步减少边界错误。
5. 输出与构建相关:决定“编译产物长什么样”
outDir
指定编译输出目录,比如 dist。如果不写,.js 会默认输出到源文件旁边。它的目的就是把源码和构建产物分开。
rootDir
指定源码根目录,主要影响输出目录结构。官方特别强调:rootDir 不会影响哪些文件被纳入编译,它不参与 include / exclude / files 的匹配。它的目的主要是控制输出目录层级。
noEmit
只做类型检查,不产出 JS、source map、声明文件。官方明确说,这很适合把真正转译工作交给 Babel、swc、bundler,而 TS 只负责类型检查。现代前端项目非常常见。
noEmitOnError
有类型错误时不输出构建产物。目的就是避免带着类型错误把产物发出去。
sourceMap
生成 sourcemap,让调试器能映射回原始 TypeScript 源码。目的就是方便开发调试和报错定位。
declaration
生成 .d.ts 声明文件。这个对发布 npm 库尤其重要,因为声明文件描述的是模块对外暴露的 API 类型。
declarationMap
给 .d.ts 再生成映射,编辑器可直接从声明跳到原始 .ts。官方特别建议:如果在用 project references,强烈考虑开启。
incremental
保存上次编译的项目图信息到磁盘,生成 .tsbuildinfo,从而加快后续构建。目的就是提升大项目的二次编译速度。
composite
给项目引用 / tsc --build 场景用。它会施加一些约束,让构建工具能更快判断项目是否已构建,并且会要求实现文件都被 include 或 files 明确覆盖。目的就是支撑可组合、可缓存的多项目构建。
skipLibCheck
跳过 .d.ts 声明文件检查,能节省编译时间,但官方也明确提示这是以类型精度为代价的;例如依赖树里存在两份不一致的类型定义时,它可能掩盖问题。它的目的通常是换速度,但不宜盲开成长期方案。
6. 另外一个很常见但容易忽略的选项
isolatedModules
当你的代码会被单文件转译器处理时很常见,比如 Babel、swc、esbuild 某些模式。官方说明它会在你写出“不能被单文件正确理解”的 TS 代码时给出警告,但不会改变代码运行逻辑本身。目的就是保证代码能安全交给单文件编译链处理。
7. 一个常见的前端项目示例
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "Bundler",
"jsx": "react-jsx",
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
"noEmit": true,
"sourceMap": true,
"isolatedModules": true,
"skipLibCheck": true,
"types": ["vite/client"]
},
"include": ["src", "env.d.ts"],
"exclude": ["dist", "node_modules"]
}
这个配置背后的思路是:
前端 bundler 负责真正打包,所以 TS 用 noEmit 只做类型检查;module / moduleResolution 则尽量对齐 bundler 行为;strict 打开保证代码质量;baseUrl + paths 用来做 @/ 别名;skipLibCheck 换取更快的开发编译。上述做法与官方关于 bundler、noEmit、模块配置、严格检查和路径映射的说明是一致的。
8. 你可以这样记忆
最常用的一批,优先记这 15 个就够了:
extends、include、exclude、target、lib、module、moduleResolution、strict、baseUrl、paths、types、rootDir、outDir、noEmit、sourceMap
如果你是:
- 前端应用:重点看
module、moduleResolution、jsx、noEmit、paths - Node 服务:重点看
module、moduleResolution、target、types: ["node"] - npm 库:重点看
declaration、declarationMap、outDir - monorepo / 大仓:重点看
extends、references、composite、incremental
1. 创建基础配置
bash
npx tsc --init # 生成默认 tsconfig.json
2. 主要配置分类
2.1 编译目标相关
json
{
"compilerOptions": {
/* 语言和环境 */
"target": "es2020", // 编译目标 ES 版本
// 可选值: "es3", "es5", "es6"/"es2015", "es2020", "es2022", "esnext"
"lib": ["es2020", "dom"], // 要包含的库文件声明
// 常用: ["es6", "dom"] / ["es2020", "dom", "dom.iterable"]
"module": "commonjs", // 模块系统
// 可选值: "commonjs", "amd", "umd", "system", "es6"/"es2015", "es2020", "esnext"
"rootDir": "./src", // 源文件根目录
"outDir": "./dist", // 输出目录
"declaration": true, // 生成 .d.ts 声明文件
"declarationDir": "./types", // 声明文件输出目录
"sourceMap": true, // 生成 source map
"sourceRoot": "./src", // source map 中源文件根目录
/* 模块解析 */
"moduleResolution": "node", // 模块解析策略
"baseUrl": "./", // 解析非相对模块的基础目录
"paths": { // 路径别名映射
"@/*": ["src/*"],
"@utils/*": ["src/utils/*"]
},
"resolveJsonModule": true, // 允许导入 JSON 文件
"allowSyntheticDefaultImports": true // 允许默认导入
}
}
2.2 严格模式相关
json
{
"compilerOptions": {
/* 严格类型检查选项 */
"strict": true, // 启用所有严格类型检查
// 可以单独配置的严格模式子选项
"noImplicitAny": true, // 禁止隐式 any 类型
"strictNullChecks": true, // 严格的 null 检查
"strictFunctionTypes": true, // 严格的函数类型检查
"strictBindCallApply": true, // 严格的 bind/call/apply 检查
"strictPropertyInitialization": true, // 严格的属性初始化检查
"noImplicitThis": true, // 禁止隐式 this
"alwaysStrict": true, // 总是以严格模式解析
/* 额外检查 */
"noUnusedLocals": true, // 检查未使用的局部变量
"noUnusedParameters": true, // 检查未使用的参数
"noImplicitReturns": true, // 函数必须有返回值
"noFallthroughCasesInSwitch": true, // 防止 switch 语句穿透
"noUncheckedIndexedAccess": true, // 索引访问时包含 undefined
"exactOptionalPropertyTypes": true // 精确的可选属性类型
}
}
2.3 实验性/高级功能
json
{
"compilerOptions": {
/* 实验性选项 */
"experimentalDecorators": true, // 启用装饰器
"emitDecoratorMetadata": true, // 为装饰器生成元数据
/* 高级选项 */
"allowUnreachableCode": false, // 不允许不可达代码
"allowUnusedLabels": false, // 不允许未使用标签
"noImplicitOverride": true, // 要求显式 override 修饰符
/* 模块互操作 */
"esModuleInterop": true, // 改进 CommonJS/ES6 模块互操作
"forceConsistentCasingInFileNames": true, // 强制文件名大小写一致
/* 编辑器集成 */
"skipLibCheck": true, // 跳过库类型检查(加快编译)
"isolatedModules": true // 确保每个文件可独立编译
}
}
3. 文件包含/排除
json
{
"include": [
"src/**/*", // 包含 src 目录下所有文件
"types/**/*.d.ts", // 包含自定义类型声明
"test/**/*.ts" // 包含测试文件
],
"exclude": [
"node_modules", // 排除 node_modules
"dist", // 排除输出目录
"**/*.test.ts", // 排除测试文件(如果需要)
"**/*.spec.ts" // 排除测试文件
],
"files": [ // 明确指定要编译的文件(优先级高于 include)
"src/core.ts",
"src/main.ts"
]
}
4. 扩展配置
json
{
"extends": "./tsconfig.base.json", // 继承基础配置
"references": [ // 项目引用(用于 monorepo)
{ "path": "./shared" },
{ "path": "./packages/core" }
]
}
-
前端项目(React/Vue)
json
{
"compilerOptions": {
"target": "es2015",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true, // 与打包工具配合时设为 true
"jsx": "react-jsx" // React 17+ JSX 转换
},
"include": ["src"]
}
-
检查配置有效性
bash
npx tsc --showConfig # 显示最终配置
npx tsc --dry-run # 检查而不编译
npx tsc --noEmit # 只进行类型检查
-
watch 模式
bash
tsc --watch # 监听文件变化
tsc --project tsconfig.dev.json --watch # 指定配置文件
compilerOptions
即编译选项,主要告诉TS编译器:
- 如何编译:目标语言版本,模块系统等
- 编译什么:文件包含、排除规则
- 编译到哪里: 输出目录和格式
- 编译规则:类型检查严格程度
目标相关
{
"compilerOptions": {
"target": "es2020", // 输出什么版本的 JS
"module": "commonjs", // 使用什么模块系统
"lib": ["es2020", "dom"] // 包含哪些内置 API 定义
}
输出控制
{
"compilerOptions": {
"outDir": "./dist", // 输出目录
"outFile": "./bundle.js", // 打包成单个文件
"declaration": true, // 生成 .d.ts 声明文件
"sourceMap": true // 生成 source map
}
}
类型检查
{
"compilerOptions": {
"strict": true, // 启用所有严格检查
"noImplicitAny": true, // 禁止隐式 any
"strictNullChecks": true // 严格空值检查
}
}
模块解析
baseUrl 定义了非相对模块导入时的基础查找路径。
{
"compilerOptions": {
"baseUrl": "./", // 模块解析基础路径
"paths": { // 路径别名
"@/*": ["src/*"]
},
"moduleResolution": "node" // 使用 Node.js 解析策略
}
}
编译选项作用的时机
编译流程图解
源代码 (.ts)
↓
TypeScript 编译器 (tsc)
↓ (根据 compilerOptions 处理)
编译过程
├── 类型检查 (type checking)
├── 语法转换 (transpile)
├── 模块解析 (module resolution)
└── 代码生成 (code generation)
↓
输出文件 (.js, .d.ts, .map)
重要选项详解
1. strict 家族
json
{
"compilerOptions": {
"strict": true, // 相当于同时开启以下所有:
// "noImplicitAny": true,
// "strictNullChecks": true,
// "strictFunctionTypes": true,
// "strictBindCallApply": true,
// "strictPropertyInitialization": true,
// "noImplicitThis": true,
// "alwaysStrict": true
}
}
2. 模块相关
json
{
"compilerOptions": {
"esModuleInterop": true, // 解决 CommonJS/ES6 互操作
"allowSyntheticDefaultImports": true, // 允许默认导入
"resolveJsonModule": true // 允许导入 JSON
}
}
3. 性能优化
json
{
"compilerOptions": {
"skipLibCheck": true, // 跳过库文件检查(加快编译)
"incremental": true, // 增量编译
"tsBuildInfoFile": ".tsbuildinfo" // 增量编译信息文件
}
}
调试技巧:
bash
# 查看编译过程
tsc --listFiles # 列出所有编译文件
tsc --diagnostics # 输出诊断信息
tsc --extendedDiagnostics # 详细诊断信息