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
会降级实现遍历器。
例如index.ts
文件为:
let a: number[] = [1, 2, 3];
for (let item of a) {
console.log(item);
}
当不配置downlevelIteration
时,编译结果:
var a = [1, 2, 3];
for (var _i = 0, a_1 = a; _i < a_1.length; _i++) {
var item = a_1[_i];
console.log(item);
}
当downlevelIteration
为true
时,编译结果使用了helper
函数降级实现。
'use strict';
var __values =
(this && this.__values) ||
function (o) {
var s = typeof Symbol === 'function' && Symbol.iterator,
m = s && o[s],
i = 0;
if (m) return m.call(o);
if (o && typeof o.length === 'number')
return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
},
};
throw new TypeError(s ? 'Object is not iterable.' : 'Symbol.iterator is not defined.');
};
var e_1, _a;
var a = [1, 2, 3];
try {
for (var a_1 = __values(a), a_1_1 = a_1.next(); !a_1_1.done; a_1_1 = a_1.next()) {
var item = a_1_1.value;
console.log(item);
}
} catch (e_1_1) {
e_1 = { error: e_1_1 };
} finally {
try {
if (a_1_1 && !a_1_1.done && (_a = a_1.return)) _a.call(a_1);
} finally {
if (e_1) throw e_1.error;
}
}
isolatedModules
在typescript
中,默认情况下却将所有的 *.ts
文件作用域放到了一起。所以即使不同文件有同名变量也会报错。此时可以将isolatedModules
设为true
,这样就可以隔离各个文件的作用域。
|-- src
|-- index.ts
|-- exp1.ts
例如在src
目录下的index.ts
和exp1.ts
文件中都定义了一个变量test
,此时编译器会提示错误:Cannot redeclare block-scoped variable test
。
除了使用isolatedModules
字段外,还可以使用export { test }
或者export defalut test
,让ts
文件成为一个模块,这样也可以达到隔离作用域的效果。
Strict Type-Checking Options
与严格类型检查相关的配置项一共有以下八项:
// "strict": true, // 开启所有严格的类型检查
// "noImplicitAny": false, // 不允许隐式的 any 类型
// "strictNullChecks": false, // 不允许把 null、undefined 赋值给其他类型变量
// "strictFunctionTypes": false // 不允许函数参数双向协变
// "strictPropertyInitialization": false, // 类的实例属性必须初始化
// "strictBindCallApply": false, // 严格的 bind/call/apply 检查(参数类型相同)
// "noImplicitThis": false, // 不允许 this 有隐式的 any 类型,避免this指向全局
// "alwaysStrict": false, // 在代码中注入 "use strict";
strict
strict
的默认值为false
,当strict
配置为true
时,其它七项默认为开启状态,当配置为false
时,其它七项默认为关闭状态,也就是说其它七项是strict
的子集。
当strict
为false
时,可以根据自己的需求,通过设置其它几项自定义类型检查。
noImplicitAny
当noImplicitAny
的值为true