TypeScript tsconfig.json Usage
在TypeScript项目中,tsconfig.json文件指定了用来编译这个项目的根文件和编译选项,通过自定义tsconfig.json文件中的配置项,可以达到我们想要的编译结果。
tsc
当我们使用tsc命令对项目进行编译时,编译器会从当前目录开始去查找tsconfig.json文件,逐级向上搜索父目录。
下面我们将通过以下三个方面来讲述tsconfig.json配置:
- 文件选项:
files、include、exclude - 编译选项:
compilerOptions - 项目引用:
extends、references
文件选项
files
files指定一个包含相对或绝对文件路径的列表,列举在files中的所有文件,编译器在编译时都会将它们包含在内。
// tsconfig.json
"files": [
"src/core.ts",
"src/index.ts",
]
当配置文件中的files字段值如上所示时,使用tsc命令编译时,src目录下的core.ts和index.ts文件会被编译为core.js和index.js文件。
include
include指定编译的文件或目录,include是支持使用通配符来匹配路径名,支持的通配符及其功能如下表所示:
| 通配符 | 功能 |
|---|---|
| * | 匹配0或多个字符,但是不匹配"."开头的隐藏文件和目录分隔符(/ or \ ) |
| ? | 匹配一个任意字符,但是不匹配目录分隔符 |
| ** | 匹配0或多个字符,包括斜杠(这意味着可以匹配多个目录) |
例如,编译src下所有文件:
"include": [
"src",
]
只编译src二级目录下的文件:
"include": [
"src/*/*",
]
当tsconfig.json文件同时配置了files和 include 字段时,编译器会将它们进行合并。当files和 include中配置的的文件所引用的文件不在其中时,被引用的文件也会被包含进来。
exclude
exclude指定编译时排除的文件或目录,exclude的用法与include相同。
当没有配置files和 include 字段时,编译器会默认编译当前目录和子目录下所有的ts文件(.ts, .d.ts 和 .tsx),排除在exclude里配置的文件。而当设置了files和 include 字段时,可以通过exclude字段过滤include的文件。
需要注意的是:通过 files属性明确指定的文件却总是会被编译,不管exclude如何配置。
此外,exclude默认情况下会排除node_modules,bower_components,jspm_packages和输出目录。
compilerOptions
compilerOptions(编译选项)是tsconfig.json文件中的重要组成部分,通过配置compilerOptions中的属性可以实现项目的定制化编译。当tsconfig.json文件中不包含compilerOptions对象时,编译器编译时会使用默认值。
本文将compilerOptions中的属性划分为以下四类进行讲解:
Basic Options
incremental
incremental增量编译,默认值为true,作用是加快编译速度。
当该配置为 true时,会保存 ts 最后一次编译的信息,信息保存在根目录下的 .tsbuildinfo 文件中。下一次编译时,编译器会根据 .tsbuildinfo 文件中的信息判断出编译的最小代价。
与incremental相关的字段为tsBuildInfoFile,通过该字段可以指定编译信息保存在自定义的文件。
{
"compilerOptions": {
"incremental": true, /* Enable incremental compilation */
"tsBuildInfoFile": "./buildcache/front-end" /* Specify file to store incremental compilation information */
}
}
target
target编译后目标语言的版本,默认值为ES3(不区分大小写)。除了设置为ES3外,还可以设置为ES5, ES2015,......, ES2020, or ESNEXT。其中ESNEXT总是指向最新的js版本,随着版本更新的变化而变化。但是通常我们都将其设置为ES5。
{
"compilerOptions": {
"target": "es5"
}
}
module
module是指生成代码的模块标准,当target的值为es6时,module的默认值为es6,否则默认为commonjs。其中es6是js模块化规范,commonjs是node.js的模块化规范。除了默认的这两个之外,它的值还可以是 'amd', 'umd'等。关于模块化的内容推荐学习 javaScript:模块机制。
下面我们通过一个ts文件,列举了不同module值编译后的结果:
ts文件代码(ES6 规范):
const sum = (a: number, b: number) => a + b;
export default sum;
module: es6
const sum = (a, b) => a + b;
export default sum;
module: commonjs
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const sum = (a, b) => a + b;
exports.default = sum;
module: umd
(function (factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
} else if (typeof define === 'function' && define.amd) {
define(['require', 'exports'], factory);
}
})(function (require, exports) {
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const sum = (a, b) => a + b;
exports.default = sum;
});
amd不经常使用,但可以结合outFile字段,将多个相互依赖的文件生成一个文件。
{
"compilerOptions": {
"module": "amd",
"outFile": "./app.js"
}
}
当执行tsc命令时,会将本目录下的文件一起整合到./app.js中。
lib
lib是指TS编译需要引用的库。当target 设置为较低版本JS,却在代码中使用更高版本的API时,代码编译会出错。
例如 index.ts 的内容为:
let myname: string | undefined = ['robbie', 'peter'].find(item => item === 'robbie');
使用tsc index.js --target es5编译时会编译失败。
error TS2339: Property 'find' does not exist on type 'string[]'.
此时如果使用tsc index.js --target es5 --lib es6运行,代码将编译成功。
原因:虽然TS是JS的超集,但是仅仅是相对于JS的语法来说。对于JS各类型的API需要引用类库(lib)来支持。所以当代码中使用了JS较高版本的API时,最好在lib字段上添加相应版本。
建议配置:
{
"compilerOptions": {
"target": "es5",
"lib": ["dom","dom.iterable","esnext"],
}
}
outDir
outDir表示编译后的输出目录。
"outDir": './dist'
当将outDir设置为./dist后,编译后的文件将输出到./dist目录下。
rootDir
rootDir通过设置输入文件的目录控制输出文件的目录结构,默认输入文件目录为当前目录。
当compilerOptions的配置如下时:
{
"compilerOptions": {
"outDir": './dist',
"rootDir": './src'
}
}
编译后的目录结构:
|-- dist <========= 输出目录
|-- src
|-- ex1.js
|-- index.js
|-- src
|-- ex1.ts
|-- index.ts
由编译目录可以看到,index.ts文件虽然不在src下,但是依旧被编译了,但是执行会打印错误提示信息,建 议将所有文件包含在rootDir下。
error TS6059: File 'D:/Code/ts/index.ts' is not under 'rootDir' 'D:/Code/ts/src'. 'rootDir' is expected to contain all source files.
如果将index.ts文件放入src文件下再编译,文件目录如下:
|-- dist <========= 输出目录
|-- ex1.js
|-- index.js
|-- src
|-- ex1.ts
|-- index.ts
可以看出dist文件下的目录发生了改变,文件中没有包含src。
allowJs
allowJs默认值为false,当设置它的值为true时,编译时允许编译 JS 文件。
allowJs: false
|-- dist
|-- ex1.js
|-- src
|-- ex1.ts
|-- ex2.js <= allowJs: false 时 js 文件不编译
allowJs: true
|-- dist
|-- ex1.js
|-- ex2.js <= allowJs: true 时 js 文件也编译了
|-- src
|-- ex1.ts
|-- ex2.js
将 js 工程升级为 ts 工程时,在将所有 js 结尾的文件改成 ts 结尾的同时要注意 ts 文件引用 js 文件的情形。此时引用的 js 文件也需要编译导出。
checkJs
checkJs默认值为false,当值为true时允许在js文件中进行类型检查和报错。但checkJs需要配合allowJs一起使用,当allowJs为false时,checkJs即使为true也不会起作用。
例如当没有配置outDir且allowJs的值为true时,编译器会编译js文件,编译后的 js 文件会覆盖源文件,此时如果checkJs的值为true时,编译时会报错。
{
"compilerOptions": {
// "outDir": './dist',
"allowJs": true,
"checkJs": true
}
}
jsx
当使用ts开发时,通常都是以.ts为后缀。但是当我们开发react的时候,react组件一般会以.tsx作为文件的后缀,此时便需要对jsx属性进行配置。
jsx属性有三个值:
| jsx 值 | 输入 | 输出 | 输出文件扩展名 |
|---|---|---|---|
| preserve | <div /> | <div /> | .jsx |
| react | <div /> | React.createElement("div") | .js |
| react-native | <div /> | <div /> | .js |
declaration
declaration为true时,编译后会为每个 ts 文件生成对应的声明文件(.d.ts 文件) 。
原目录:
|-- src
|-- index.ts
编译后目录:
|-- dist
|-- index.js
|-- index.d.ts <= 自动生成的类型声明文件
|-- src
|-- index.ts
声明文件默认会和编译后的js文件保存在同一目录,但是我们可以在compilerOptions中配置declarationDir属性指定声明文件的输出目录。
当我们只想编译生成.d.ts文件而不想编译生成js文件时,可以在compilerOptions中配置 emitDeclarationOnly为true。
在项目中使用工程引用时,必须在根 tsconfig.json 中配置declaration。
sourceMap
sourceMap为true时,ts编译时会生成对应的.js.map文件。 .js.map文件是一个信息文件,里面储存着位置信息,记录着编译文件前后的位置关系。当编译后的js文件出错时,可以更方便的定位到ts文件的错误位置。
原目录:
|-- src
|-- index.ts
编译后目录:
|-- dist
|-- index.js
|-- index.js.map <= 编译后生成对应的 *.js.map 文件
|-- src
|-- index.ts
declarationMap
与souceMap类似,declarationMap会为声明文件生成对应的.d.ts.map文件。
removeComments
removeComments的值为true时,编译后的js文件会删除源文件中的注释。
noEmit
noEmit为 true时,使用 tsc 编译后不会输出文件。
与之相关的属性有noEmitOnError,noEmitOnError为true时,在编译发生错误时不会输出文件。
importHelpers
当ts中使用类继承等操作时,编译后的js文件需要用到一些公共方法(helper),每个文件中用到都重新定义一遍是很浪费且很占用 空间的。所以当我们配置importHelpers为true时,编译后的文件将不再生成helper函数。
例如当index.ts文件内容如下时:
class A {}
class B extends A {}
export = B
编译后 helper 函数为:
var __extends =
(this && this.__extends) ||
(function () {
var extendStatics = function (d, b) {
extendStatics =
Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array &&
function (d, b) {
d.__proto__ = b;
}) ||
function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
};
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() {
this.constructor = d;
}
d.prototype = b === null ? Object.create(b) : ((__.prototype = b.prototype), new __());
};
})();
配置importHelpers为true后,编译后的代码会引用tslib包,tslib包中包含__extends方法。
var tslib_1 = require("tslib");
...
var B =(function (_super){
tslib_1.__extends(B,_super); <=== tslib_1中包含__extends方法
...
}(A))
downlevelIteration
当 target 为 es5 或 es3 时,downlevelIteration为true会降级实现遍历器。