Menu

tsconfig.json 详解:配置选项、默认值与示例

tsconfig.json 把一个文件夹标记为 TypeScript 项目,并设置编译选项。本文讲解重要的选项(target、module、moduleResolution、strict、rootDir、outDir、include、lib、types、noEmit、skipLibCheck)、推荐的起步配置、extends,以及 TypeScript 7 带来的变化。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

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 解析它的工具可能会因此出错。

Coddy 编程语言插图

和 Coddy 一起学编程

开始