tsconfig.json 指南
项目根目录标识
tsconfig.json
是 TypeScript 项目的配置文件,它在项目中扮演着非常重要的角色。当一个目录中存在 tsconfig.json
文件时,TypeScript 编译器会将其视为项目的根目录。这意味着,编译器会从这个文件所在的位置开始,按照配置文件中的指令来编译项目中的 TypeScript 文件。
例如,如果你的项目结构如下:
demo/
├── src/
│ ├── index.ts
├── tsconfig.json
└── package.json
那么,tsconfig.json
所在的 demo 就是项目的根目录。当你运行 tsc
命令时,编译器会自动查找 tsconfig.json
文件,并根据其中的配置来编译 src
目录下的 .ts
文件。如果没有 tsconfig.json
文件,TypeScript 编译器会逐级向上查找,直到找到一个 tsconfig.json
文件为止。
自动生成与继承
tsconfig.json
文件可以通过命令行工具自动生成,也可以通过继承其他配置文件来实现复用。使用 tsc --init
命令可以快速生成一个默认的 tsconfig.json
文件,这个文件包含了 TypeScript 编译器的一些常用配置项。例如:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": [
"src/**/*"
]
}
这个默认生成的文件已经包含了大多数项目的基本配置,你可以根据项目的具体需求进行修改和调整。 除了自动生成,tsconfig.json
还支持继承其他配置文件。这在大型项目中非常有用,特别是当项目中有多个子项目时。你可以将公共的配置项放在一个基础配置文件中,然后在各个子项目的 tsconfig.json
文件中通过 extends
属性来继承这些公共配置。
例如,你有一个基础配置文件 tsconfig.base.json
:
{
"compilerOptions": {
"target": "es6",
"module": "esnext",
"strict": true
}
}
然后在子项目的 tsconfig.json
文件中继承它:
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist"
},
"include": [
"src/**/*"
]
}
这样,子项目就会继承基础配置文件中的所有配置项,并且还可以添加或覆盖一些特定的配置。这种继承机制可以大大减少配置的重复,提高项目的可维护性。
顶层配置项
extends
extends
是 tsconfig.json
文件中非常重要的一个配置项,它允许一个配置文件继承另一个配置文件的内容。
这在大型项目中非常有用,尤其是当项目中有多个子项目时,可以将公共的配置项放在一个基础配置文件中,然后在各个子项目的 tsconfig.json
文件中通过 extends
属性来继承这些公共配置。
例如,你有一个基础配置文件 tsconfig.base.json
:
{
"compilerOptions": {
"target": "es6",
"module": "esnext",
"strict": true
}
}
然后在子项目的 tsconfig.json
文件中继承它:
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist"
},
"include": [
"src/**/*"
]
}
这样,子项目就会继承基础配置文件中的所有配置项,并且还可以添加或覆盖一些特定的配置。这种继承机制可以大大减少配置的重复,提高项目的可维护性。 需要注意的是,如果 extends
属性指定的路径不是以 ./
或 ../
开头,那么编译器将在 node_modules
目录下查找指定的配置文件。此外,extends
指定的配置文件会先加载,然后加载当前的 tsconfig.json
文件。如果两者有重名的属性,后者会覆盖前者。
files
files
属性用于显式指定需要编译的文件列表。它是一个数组,其中包含文件的相对或绝对路径。如果某个文件被列在 files
中,那么它会被编译器包含在编译过程中,即使它在 exclude
列表中也是如此。例如:
{
"files": [
"src/index.ts",
"src/utils.ts"
]
}
在这个例子中,只有 src/index.ts
和 src/utils.ts
会被编译器处理。如果 files
和 include
都没有被指定,编译器默认包含当前目录和子目录下所有的 TypeScript 文件(.ts
、.d.ts
和 .tsx
),排除在 exclude
里指定的文件。如果指定了 files
或 include
,编译器会将它们结合一并包含进来。
需要注意的是,files
中列出的路径必须是指定文件,而不是某个文件夹,而且不能使用 *
、?
、**
等通配符。此外,任何被 files
或 include
指定的文件所引用的文件也会被包含进来。例如,如果 A.ts
引用了 B.ts
,那么 B.ts
也会被编译器包含,除非引用它的 A.ts
在 exclude
列表中。
include
include
属性用于指定需要编译的文件或目录的模式匹配列表。它支持使用通配符,例如:
- *:匹配0或多个字符(不包括目录分隔符)
- ?:匹配一个任意字符(不包括目录分隔符)
- **/:递归匹配任意子目录
{
"include": [
"src/**/*"
]
}
这个配置表示编译器会包含 src
目录下的所有文件(包括子目录中的文件)。如果 include
和 exclude
都被指定,编译器会先根据 include
包含文件,然后根据 exclude
排除文件。 需要注意的是,include
中的路径可以是文件或目录,可以使用相对或绝对路径。此外,如果没有特殊指定,exclude
默认情况下会排除 node_modules
、bower_components
、jspm_packages
和 outDir
目录。
exclude
exclude
属性用于指定需要从编译中排除的文件或目录的模式匹配列表。它也支持使用通配符,与 include
的通配符规则相同。例如:
{
"exclude": [
"node_modules",
"dist"
]
}
这个配置表示编译器会排除 node_modules
和 dist
目录下的所有文件。需要注意的是,exclude
只对 include
包含的文件有效。如果某个文件被显式地列在 files
中,那么它不会被 exclude
排除。
此外,exclude
中的路径可以是文件或目录,可以使用相对或绝对路径。如果没有特殊指定,exclude
默认情况下会排除 node_modules
、bower_components
、jspm_packages
和 outDir
目录。
编译选项 compilerOptions
target
target
是 compilerOptions
中非常重要的一个配置项,它用于指定编译后的 JavaScript
文件的目标 ECMAScript
版本。不同的目标版本会影响代码的兼容性和性能。
- 支持的版本:
target
支持的版本包括es3
、es5
、es6
(或es2015
)、es2016
、es2017
、es2018
、es2019
、es2020
、es2021
、es2022
和esnext
。例如,如果你设置为 "target
": "es5
",编译器会将 TypeScript 代码转换为兼容 ES5 的 JavaScript 代码。 - 默认值:如果没有指定
target
,默认值是es3
。但在现代项目中,通常建议至少设置为es5
,因为 ES5 已经被广泛支持,且提供了更好的性能和特性。 - 实际影响:选择不同的
target
会对编译结果产生显著影响。例如,如果你的目标环境是较新的浏览器或 Node.js 环境,可以选择es2015
或更高版本,这样可以利用现代 JavaScript 的新特性,如箭头函数、解构赋值等。但如果需要兼容旧的浏览器(如 IE11),则需要设置为es5
或es3
。
module
module
配置项用于指定编译后的 JavaScript 模块格式。不同的模块格式适用于不同的运行环境。
- 支持的模块格式:
module
支持的模块格式包括none
、commonjs
、amd
、umd
、system
、es6
(或es2015
)、es2020
、es2022
、esnext
、node16
和nodenext
。例如,如果你设置为 "module
": "commonjs",编译器会将 TypeScript 代码转换为 CommonJS 模块格式,这在 Node.js 环境中非常常用。 - 默认值:如果没有指定
module
,默认值取决于target
。如果target
是ES3
或ES5
,默认值是commonjs
;否则默认值是ES6
。 - 实际影响:选择合适的模块格式对项目的运行环境至关重要。例如,如果你的项目运行在浏览器中,可以选择
umd
或es6
;如果运行在 Node.js 环境中,commonjs 是一个不错的选择。不同的模块格式会影响代码的加载方式和执行效率。
strict
strict
是一个非常有用的配置项,它用于启用一系列严格的类型检查选项,帮助开发者编写更安全、更可靠的代码。
- 启用严格模式:当设置为 "
strict
":true
时,TypeScript 会启用以下严格的类型检查选项:noImplicitAny
:不允许在没有明确指定类型的情况下使用any
类型。noImplicitThis
:不允许在没有明确指定this
类型的情况下使用this
。strictNullChecks
:启用严格的空值检查,不允许将null
或undefined
赋值给其他类型的变量。strictFunctionTypes
:启用严格的函数类型检查,防止函数参数双向协变。strictPropertyInitialization
:确保类的非undefined
属性在构造函数中初始化。
- 默认值:如果没有指定
strict
,默认值是false
,即不启用严格的类型检查。 - 实际影响:启用
strict
模式可以显著提高代码的质量和安全性。虽然它可能会增加一些开发成本,但长远来看,它有助于减少潜在的错误和问题,特别是在大型项目中。
outDir
outDir
配置项用于指定编译后的 JavaScript 文件的输出目录。通过合理配置 outDir
,可以更好地组织项目的文件结构。
- 指定输出目录:例如,如果你设置为 "
outDir
": "./dist
",编译器会将所有编译后的 JavaScript 文件输出到项目根目录下的dist
文件夹中。 - 默认值:如果没有指定
outDir
,编译器会将编译后的文件输出到与源文件相同的目录中。这可能会导致源代码和编译后的代码混在一起,不利于项目的维护。 - 实际影响:合理配置
outDir
可以帮助你更好地管理项目的文件结构,避免源代码和编译后的代码混淆。同时,它也有助于自动化工具(如 Webpack)更好地处理项目的构建过程。
文件处理配置
allowJs
allowJs
是 compilerOptions
中的一个配置项,用于指定是否允许编译器编译 .js
和 .jsx
文件。默认情况下,TypeScript 编译器只处理 .ts
和 .tsx
文件,但通过设置 allowJs
为 true
,可以将 .js
和 .jsx
文件也纳入编译范围。
实际作用:当你需要在 TypeScript 项目中同时处理 JavaScript 文件时,这个配置项非常有用。
例如,你可能有一些遗留的 JavaScript 代码需要逐步迁移到 TypeScript,或者你希望在项目中使用一些 JavaScript 库。通过设置 "allowJs
": true
,TypeScript 编译器会将 .js
文件原样输出到指定的 outDir
目录中,而不会对其进行类型检查。
project-root/
├── src/
│ ├── index.ts
│ └── utils.js
├── tsconfig.json
└── package.json
在 tsconfig.json
中设置:
{
"compilerOptions": {
"allowJs": true,
"outDir": "./dist"
},
"include": [
"src/**/*"
]
}
那么,utils.js
会被编译器原样输出到 dist
目录中,而 index.ts
会被编译为 JavaScript 并输出到 dist
目录。
checkJs
checkJs
是 compilerOptions
中的一个配置项,用于指定是否对 .js
文件进行类型检查。默认情况下,TypeScript 编译器不会对 .js
文件进行类型检查,但通过设置 checkJs
为 true
,可以启用对 .js
文件的类型检查。
实际作用:当你希望在 JavaScript 文件中也享受 TypeScript 的类型检查功能时,这个配置项非常有用。它可以帮助你在 JavaScript 代码中发现潜在的类型错误,提高代码质量。
**需要注意的是,启用 checkJs
时,allowJs
也必须为 true
,否则编译器不会处理 .js 文件。
假设项目结构如下:
project-root/
├── src/
│ ├── index.ts
│ └── utils.js
├── tsconfig.json
└── package.json
在 tsconfig.json
中设置:
{
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"outDir": "./dist"
},
"include": [
"src/**/*"
]
}
那么,utils.js
不仅会被原样输出到 dist
目录中,还会进行类型检查。如果 utils.js
中存在类型错误,编译器会报错。
declaration
declaration
是 compilerOptions
中的一个配置项,用于指定是否为每个 .ts
文件生成对应的 .d.ts
声明文件。默认情况下,declaration
为 false
,即不生成声明文件。
实际作用:当你需要为你的 TypeScript 项目生成类型声明文件时,这个配置项非常有用。声明文件 .d.ts
可以被其他项目引用,从而在不包含源代码的情况下,提供类型信息。这对于开发库或框架非常有用,因为你可以将声明文件发布到 npm,供其他开发者使用。
假设项目结构如下:
project-root/
├── src/
│ ├── index.ts
│ └── utils.js
├── tsconfig.json
└── package.json
tsconfig.json
中设置:
{
"compilerOptions": {
"declaration": true,
"outDir": "./dist",
"declarationDir": "./types"
},
"include": [
"src/**/*"
]
}
那么,编译器会为 src
目录下的每个 .ts
文件生成对应的 .d.ts
文件,并将它们输出到 types
目录中。例如,src/index.ts
会生成 types/index.d.ts
,src/utils.ts
会生成 types/utils.d.ts
。
模块与路径配置
moduleResolution
moduleResolution
是 compilerOptions
中用于指定模块解析策略的配置项。它决定了 TypeScript 编译器如何查找模块文件,这对于项目中模块的加载和运行至关重要。
支持的值:
node
:这是默认值,也是最常用的模块解析策略。它模拟了 Node.js 的模块解析方式,会按照node_modules
目录的结构来查找模块。例如,当你导入一个模块import * as module from 'some-module'
时,编译器会先在当前目录下的node_modules
文件夹中查找some-module
,如果找不到,会逐级向上查找,直到找到为止。classic
:这是 TypeScript 早期的模块解析策略,主要用于支持旧版本的 TypeScript 项目。它会按照文件路径的顺序来查找模块,而不是按照node_modules
的结构。例如,如果导入路径是相对路径,编译器会直接在当前目录下查找对应的文件。bundler
:这种策略主要用于支持打包工具(如 Webpack)的模块解析方式。它假设模块已经被打包工具处理过,因此不会按照常规的文件路径来查找模块,而是直接使用打包后的路径。这种策略适用于使用打包工具进行构建的项目,可以提高编译速度并减少对源文件的依赖。
实际影响:选择合适的模块解析策略可以确保模块的正确加载和运行。例如,在大多数现代项目中,推荐使用
node
模块解析策略,因为它与 Node.js 的生态系统兼容性最好。如果你的项目使用了打包工具,可以考虑使用bundler
策略来优化编译性能。
baseUrl
baseUrl
是 compilerOptions
中用于指定模块解析的基准目录的配置项。它通常与 paths
配置项一起使用,用于配置模块的路径映射。
实际作用:当你使用相对路径导入模块时,
baseUrl
指定了这些相对路径的起点。例如,如果你设置"baseUrl": "./"
,那么当你导入模块import * as module from 'some-module'
时,编译器会从项目根目录开始查找some-module
。如果没有设置baseUrl
,编译器会默认以tsconfig.json
文件所在的目录为基准。案例:假设你的项目结构如下:
project-root/ ├── src/ │ ├── index.ts │ └── utils/ │ └── helper.ts ├── tsconfig.json └── package.json
如果你在
tsconfig.json
中设置:{ "compilerOptions": { "baseUrl": "./src", "moduleResolution": "node" } }
那么,当你在
src/index.ts
中导入utils/helper.ts
时,可以使用相对路径import * as helper from 'utils/helper'
,编译器会从src
目录开始查找utils/helper.ts
。
paths
paths
是 compilerOptions
中用于配置模块路径映射的配置项。它通常与 baseUrl
配合使用,允许你自定义模块的解析路径。
实际作用:
paths
允许你为模块名指定一个或多个路径,从而实现模块的别名映射。这对于大型项目中模块的组织和管理非常有用,可以简化模块的导入路径,提高代码的可读性和可维护性。例如,你可以为src
目录下的模块设置别名,或者为第三方库指定特定的版本路径。案例:假设你的项目结构如下:
project-root/ ├── src/ │ ├── index.ts │ └── utils/ │ └── helper.ts ├── node_modules/ │ └── some-library/ │ └── index.js ├── tsconfig.json └── package.json
如果你在
tsconfig.json
中设置:{ "compilerOptions": { "baseUrl": "./src", "paths": { "@utils/*": ["utils/*"], "@library": ["../node_modules/some-library"] } } }
那么,你可以在
src/index.ts
中使用别名导入模块:import * as helper from '@utils/helper'; import * as library from '@library';
编译器会根据
paths
配置将@utils/helper
解析为src/utils/helper
,将@library
解析为node_modules/some-library
。# 6. 性能与辅助配置
incremental
incremental
是 compilerOptions
中用于优化编译性能的配置项。它通过启用增量编译功能,帮助加快大型项目的编译速度。
- 实际作用:当设置为
"incremental": true
时,TypeScript 编译器会在第一次编译后生成一个tsbuildinfo
文件,该文件存储了编译过程中的信息。在后续的编译中,编译器会利用这些信息,只重新编译那些自上次编译以来发生更改的文件,从而显著减少编译时间。例如,对于一个包含 100 个文件的项目,如果只修改了其中一个文件,增量编译只会重新编译这个修改过的文件及其依赖的文件,而不是整个项目。 - 案例:假设你的项目结构如下:如果你在
project-root/ ├── src/ │ ├── index.ts │ └── utils.ts ├── tsconfig.json └── package.json
tsconfig.json
中设置:那么,当你第一次运行{ "compilerOptions": { "incremental": true, "outDir": "./dist" }, "include": [ "src/**/*" ] }
tsc
命令时,编译器会生成一个tsbuildinfo
文件。在后续的编译中,如果你只修改了utils.ts
,编译器只会重新编译utils.ts
和依赖它的文件,而不是整个项目。
sourceMap
sourceMap
是 compilerOptions
中用于调试的配置项。它允许生成源代码映射文件(.js.map
),这些文件将编译后的 JavaScript 代码与原始的 TypeScript 源代码关联起来。
- 实际作用:当设置为
"sourceMap": true
时,TypeScript 编译器会为每个编译后的 JavaScript 文件生成一个对应的.js.map
文件。这些文件包含了映射信息,使得开发者可以在浏览器的开发者工具中直接调试 TypeScript 源代码,而不是编译后的 JavaScript 代码。这对于调试大型项目非常有用,因为它可以让你更直观地查看和调试代码。 - 案例:假设你的项目结构如下:如果你在
project-root/ ├── src/ │ ├── index.ts │ └── utils.ts ├── tsconfig.json └── package.json
tsconfig.json
中设置:那么,编译器会为{ "compilerOptions": { "sourceMap": true, "outDir": "./dist" }, "include": [ "src/**/*" ] }
src/index.ts
和src/utils.ts
分别生成dist/index.js.map
和dist/utils.js.map
文件。当你在浏览器中打开项目并使用开发者工具时,你可以直接看到和调试 TypeScript 源代码,而不是编译后的 JavaScript 代码。
emitDeclarationOnly
emitDeclarationOnly
是 compilerOptions
中用于优化编译输出的配置项。它允许只生成类型声明文件(.d.ts
),而不生成 JavaScript 文件。
- 实际作用:当设置为
"emitDeclarationOnly": true
时,TypeScript 编译器只会生成.d.ts
文件,而不会生成.js
文件。这对于开发库或框架非常有用,因为你可以将声明文件发布到npm
,供其他开发者使用,而不需要包含实际的 JavaScript 实现代码。这可以减少包的大小,同时提供完整的类型信息。 - 案例:假设你的项目结构如下:如果你在
project-root/ ├── src/ │ ├── index.ts │ └── utils.ts ├── tsconfig.json └── package.json
tsconfig.json
中设置:那么,编译器会为{ "compilerOptions": { "emitDeclarationOnly": true, "declaration": true, "declarationDir": "./types" }, "include": [ "src/**/*" ] }
src/index.ts
和src/utils.ts
分别生成types/index.d.ts
和types/utils.d.ts
文件,而不会生成对应的.js
文件。这样,你就可以将这些声明文件发布到npm
,供其他开发者使用。
类型检查与代码规范
noImplicitAny
noImplicitAny
是 compilerOptions
中用于增强类型安全性的配置项。当设置为 true
时,TypeScript 编译器会禁止在没有明确指定类型的情况下使用 any
类型。
- 实际作用:在开发过程中,
any
类型可能会导致类型安全问题,因为它可以表示任何类型,从而绕过类型检查。启用noImplicitAny
可以帮助开发者在编写代码时更明确地指定类型,减少潜在的错误。例如,当你声明一个变量但没有指定其类型时,编译器会报错,提示你需要明确指定类型。 - 案例:假设你有以下代码:如果在
let x; // 没有指定类型 x = 123; x = "hello";
tsconfig.json
中设置:那么,编译器会报错,提示变量{ "compilerOptions": { "noImplicitAny": true } }
x
的类型没有明确指定。你需要显式地指定类型,例如:let x: number | string; x = 123; x = "hello";
noUnusedLocals
noUnusedLocals
是 compilerOptions
中用于优化代码质量的配置项。当设置为 true
时,TypeScript 编译器会报错,提示存在未使用的局部变量。
- 实际作用:在大型项目中,未使用的变量可能会导致代码混乱和维护困难。启用
noUnusedLocals
可以帮助开发者及时发现并清理这些未使用的变量,从而提高代码的可读性和可维护性。例如,当你声明了一个变量但没有在代码中使用它时,编译器会报错。 - 案例:假设你有以下代码:如果在
function example() { let unusedVariable = 42; console.log("Hello, world!"); }
tsconfig.json
中设置:那么,编译器会报错,提示变量{ "compilerOptions": { "noUnusedLocals": true } }
unusedVariable
未被使用。你需要删除或使用这个变量,例如:function example() { let usedVariable = 42; console.log(usedVariable); }
noImplicitReturns
noImplicitReturns
是 compilerOptions
中用于增强代码规范的配置项。当设置为 true
时,TypeScript 编译器会报错,提示函数的所有可能路径都必须有明确的返回值。
- 实际作用:在开发过程中,函数的某些分支可能没有返回值,这可能会导致代码行为不符合预期。启用
noImplicitReturns
可以帮助开发者确保每个函数的所有分支都有明确的返回值,从而提高代码的可读性和可维护性。例如,当你编写一个函数但某些分支没有返回值时,编译器会报错。 - 案例:假设你有以下代码:如果在
function example(condition: boolean): string { if (condition) { return "Condition is true"; } // 缺少返回值 }
tsconfig.json
中设置:那么,编译器会报错,提示函数{ "compilerOptions": { "noImplicitReturns": true } }
example
的某些分支没有返回值。你需要为所有分支添加返回值,例如:function example(condition: boolean): string { if (condition) { return "Condition is true"; } return "Condition is false"; }