tsconfig.json 是 TypeScript 项目的配置文件。不带参数运行 tsc 时,编译器会在当前文件夹中查找它(找不到再去上级文件夹找),读取 compilerOptions 中的选项,并检查 include 列出的文件。下面是一个小巧但完整的例子:
{
"compilerOptions": {
"target": "es2022",
"module": "nodenext",
"strict": true,
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["src"]
}
这会把 src 中的所有 TypeScript 文件编译成 dist 中的 JavaScript,输出 ES2022 代码,使用 Node.js 的模块规则,并开启所有严格检查。npx tsc --init 会生成一个更长的起步文件,每个选项都附有注释。
这个文件是带注释的 JSON:// 注释、/* */ 注释和尾随逗号都可以使用。
推荐的起步配置
用于 Node.js 应用或脚本:
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"target": "es2024",
"lib": ["es2024"],
"module": "nodenext",
"types": ["node"],
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true,
"sourceMap": true
},
"include": ["src"]
}
它需要运行 npm install --save-dev @types/node 来获取 Node.js 的类型。使用 module: "nodenext" 时,package.json 中的 "type" 字段决定 .ts 文件是 ES 模块("type": "module")还是 CommonJS("type": "commonjs",npm init -y 写入的就是它)。使用 import 和 export 的新项目请用 "type": "module"。
对于由 Vite、esbuild 或 webpack 这类打包工具构建的代码,JavaScript 由打包工具生成,tsc 只负责检查:
{
"compilerOptions": {
"target": "es2022",
"lib": ["es2022", "dom"],
"module": "preserve",
"noEmit": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"skipLibCheck": true,
"jsx": "react-jsx"
},
"include": ["src"]
}
框架的项目模板(Vite、Next.js、Angular)会生成自己的 tsconfig.json。请在它们的基础上修改,而不是整个替换掉。
重要的选项
| 选项 | 作用 | TypeScript 7 默认值 |
|---|---|---|
target | 输出的 JavaScript 版本;较新的语法会为较旧的目标改写 | es2025 |
lib | 类型检查器所知道的内置 API 类型(Array.prototype.at、Map、document) | 与 target 对应,外加 DOM |
module | 输出的模块代码类型以及适用的模块规则 | esnext |
moduleResolution | 如何在磁盘上找到导入路径 | 配合对应的 module 时为 nodenext 或 node16,否则为 bundler |
strict | 开启整套严格检查 | true |
rootDir | 其目录结构会被镜像到 outDir 中的文件夹 | tsconfig.json 所在的文件夹 |
outDir | .js(以及 .d.ts)文件的输出位置 | 每个源文件的旁边 |
include / exclude / files | 哪些文件属于项目 | 文件夹下的每个 .ts 文件 |
types | 无需导入就会加载的 @types 包 | [],即不加载 |
noEmit | 只检查,不输出任何文件 | false |
declaration | 同时输出 .d.ts 类型声明文件 | false |
sourceMap | 为调试器输出 .js.map 文件 | false |
esModuleInterop | 让 import x from "cjs-package" 能用于 CommonJS 包 | true(无法关闭) |
skipLibCheck | 跳过对 .d.ts 文件的类型检查,包括 node_modules 中的文件 | false |
target 与 lib
target 指定输出代码要运行在哪个 JavaScript 版本上。比目标更新的语法会被改写:设置 "target": "es2017" 时,类字段或 ??= 会被转换成更旧的代码。TypeScript 7 接受的最低目标是 es2015(es6);es5 已被移除。
target 不会补上缺失的运行时 API。描述这些 API 是 lib 的工作,提供它们则是 polyfill 的工作。lib 告诉类型检查器有哪些内置对象和方法。它的默认值跟随 target,并且还包含浏览器的 DOM 类型,所以除非你自己设置 lib,否则即使在 Node.js 项目中,document 也能通过类型检查。使用比你的 lib 更新的标准中的方法会导致编译错误:
src/b.ts(1,24): error TS2550: Property 'toSorted' does not exist on type 'number[]'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2023' or later.
这些文档中的可运行示例使用 es2022 的 lib,所以其中无法使用 toSorted、Object.groupBy 和新的 Set 方法。
module 与 moduleResolution
对于新代码,module 有三个合理的取值:
nodenext:用于由 Node.js 运行的代码。每个文件是 ESM 还是 CommonJS,取决于它的扩展名(.mts、.cts)或最近的package.json中的"type",判断方式与 Node 完全相同。ES 模块中的相对导入需要写文件扩展名,而且要写成.js,尽管源文件是.ts:import { add } from "./math.js"。moduleResolution会自动随之设置。preserve:用于由打包工具处理的代码。导入语句保持原样,解析使用bundler规则,允许不带扩展名的导入。esnext:输出普通的 ES 模块,使用bundler解析方式。未设置module时默认就是它。
使用 tsc --init 生成的配置时,一个常见的初次错误来自 npm init -y 写入的 "type": "commonjs":
src/main.ts(1,10): error TS1295: ECMAScript imports and exports cannot be written in a CommonJS file under 'verbatimModuleSyntax'.
文件使用了 import,但 package.json 声明项目是 CommonJS。把 "type" 改成 "module" 即可。模块 一文详细介绍了导入和导出。
strict 与检查选项
"strict": true 会一次性开启一组检查:noImplicitAny、strictNullChecks、strictFunctionTypes、strictBindCallApply、strictPropertyInitialization、strictBuiltinIteratorReturn、noImplicitThis 和 useUnknownInCatchVariables。它是 TypeScript 7 的默认设置,本站每个可运行示例中也都开启了它。为严格模式编写的代码,在使用可能缺失的值之前会先收窄类型:
如果没有类型注解,参数的类型就无法推断,noImplicitAny 会报告这一点:
index.ts(2,17): error TS7006: Parameter 'n' implicitly has an 'any' type.
strict 并没有包含所有有用的检查。有两个值得额外开启:noUncheckedIndexedAccess(数组元素或索引签名读取的类型为 T | undefined)和 exactOptionalPropertyTypes(可选属性不能被显式设为 undefined);tsc --init 会把两者都打开。noUnusedLocals、noImplicitReturns 和 noFallthroughCasesInSwitch 这类风格检查默认是关闭的。
包含哪些文件:include、exclude、rootDir、outDir
如果没有 include 或 files,项目会包含该文件夹及其子文件夹中的每个 .ts、.tsx 和 .d.ts 文件。node_modules 始终会被排除。outDir 也会被排除,但前提是你没有设置 exclude:自定义的 exclude 列表会替换这个默认值,所以要把输出文件夹也加进去。include 和 exclude 接受 glob 模式:
{
"include": ["src", "scripts/**/*.ts"],
"exclude": ["src/**/*.test.ts"]
}
exclude 只过滤 include 找到的文件。被其他已包含文件导入的文件仍然会被编译。
outDir 是输出的位置,rootDir 是源代码树中被镜像到输出目录的部分:设置 "rootDir": "./src" 后,src/api/users.ts 会变成 dist/api/users.js。两者都要设置。rootDir 默认是 tsconfig.json 所在的文件夹,所以如果你只设置了 outDir,而源文件放在 src 中,TypeScript 7 会报错停止:
error TS5011: The common source directory of 'tsconfig.json' is './src'. The 'rootDir' setting must be explicitly set to this or another path to adjust your output's file layout.
输出选项:noEmit、declaration、sourceMap
"noEmit": true让tsc变成纯粹的类型检查器。当 JavaScript 由其他工具(打包工具、tsx、Node.js 类型剥离)生成时使用它。"declaration": true为每个模块输出一个.d.ts文件,只含类型,不含代码。库需要它,用户才能获得类型。declarationMap会额外生成映射文件,让“跳转到定义”能跳到.ts源代码。"sourceMap": true输出.js.map文件,让调试器和堆栈跟踪指向.ts中的行。"noEmitOnError": true在存在类型错误时不输出任何文件。没有它,tsc会报告错误,但仍然输出 JavaScript。
types、esModuleInterop 与 skipLibCheck
types 列出无需导入就会全局加载的 @types 包。TypeScript 7 的默认值是空列表,所以运行 npm install --save-dev @types/node 之后,还要加上 "types": ["node"];否则 process 和 require 仍然是未知的(error TS2591: Cannot find name 'process')。带有全局函数的测试框架(比如 Jest 的 describe)也要加到这个列表里。
esModuleInterop 让 CommonJS 包的默认导入能够正常工作(import express from "express")。它在 TypeScript 7 中始终开启,把它设为 false 会报错。
skipLibCheck 会跳过对声明文件的类型检查。它能加快构建速度,并避免 node_modules 中那些你无法修复的错误,代价是发现不了两个库的类型之间的冲突。大多数项目都会开启它。
用 extends 共享配置
extends 会加载另一个配置文件,并允许当前文件覆盖其中的部分内容:
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["src"]
}
compilerOptions 会逐个选项合并,而基础配置中的 include、exclude 和 files,如果当前文件也设置了,就会被替换而不是合并。基础配置文件中的路径是相对于基础配置文件本身解析的。extends 也接受一个包:@tsconfig/bases 项目为每种环境都发布了一个配置,例如先运行 npm install --save-dev @tsconfig/node24,再写 "extends": "@tsconfig/node24/tsconfig.json"。
要查看应用了所有 extends 和默认值之后的最终配置,运行:
npx tsc --showConfig
TypeScript 7 中移除的选项
TypeScript 6 弃用了这些设置,TypeScript 7 将它们移除了。仍在使用它们的配置会以 error TS5108 或 TS5102 失败,例如 Option 'baseUrl' has been removed. Please remove it from your configuration.
| 已移除的设置 | 替代方案 |
|---|---|
"target": "es5" | es2015 或更高版本 |
"moduleResolution": "node"(node10)或 "classic" | nodenext 或 bundler |
"module": "amd"、"umd"、"system"、"none" | nodenext、esnext 或 preserve,其他格式交给打包工具 |
baseUrl | 相对于 tsconfig 文件的 paths 条目 |
outFile | 打包工具 |
downlevelIteration | 无需替代:es2015 及以上的目标原生支持迭代 |
"esModuleInterop": false、"allowSyntheticDefaultImports": false、"alwaysStrict": false | 删除这一行;这些选项始终开启 |
TypeScript 7 一文列出了该版本的其他变化,包括这张表没有涉及的新默认值。
常见问题
tsconfig.json 是什么?
它是 TypeScript 项目的配置文件。它所在的文件夹会被视为项目根目录,compilerOptions 设置编译器如何检查和输出代码,include、exclude 或 files 指定哪些文件属于这个项目。不带参数运行 tsc 时就会读取它。
如何创建 tsconfig.json 文件?
在项目文件夹中运行 npx tsc --init(需要已安装 TypeScript)。它会生成一个带推荐设置的 tsconfig.json,每个选项都附有注释。你也可以手写这个文件;{} 就是一个合法的 tsconfig,所有选项都使用默认值。
tsconfig 中的 target 应该设置成什么?
设置成你的代码需要运行的最旧的 JavaScript 版本。对于当前的 Node.js,es2022 或更高版本是安全的;TypeScript 7 的默认值是 es2025。target 控制哪些较新的语法要为旧引擎改写,同时也决定了默认的 lib,即类型检查器所知道的内置 API 集合。
module 和 moduleResolution 有什么区别?
module 决定编译器输出哪种模块代码(ES 的 import/export 还是 CommonJS 的 require),以及适用哪套模块规则。moduleResolution 决定如何在磁盘上找到 "./utils.js" 或 "lodash" 这样的导入路径。由 Node.js 运行的代码用 nodenext,由打包工具处理的代码用 module: "preserve"(它隐含 bundler 解析方式)。
tsconfig.json 里可以写注释吗?
可以。编译器把它当作带注释的 JSON 来读取:允许 // 和 /* */ 注释以及尾随逗号,所以 tsc --init 生成的文件里满是被注释掉的选项。其他按严格 JSON 解析它的工具可能会因此出错。