Skip to main content

TypeScript tsconfig.json Usage

· 33 min read
Kimi Gao
Fullstack & AI

TypeScript项目中,tsconfig.json文件指定了用来编译这个项目的根文件和编译选项,通过自定义tsconfig.json文件中的配置项,可以达到我们想要的编译结果。

tsc

当我们使用tsc命令对项目进行编译时,编译器会从当前目录开始去查找tsconfig.json文件,逐级向上搜索父目录。

下面我们将通过以下三个方面来讲述tsconfig.json配置:

  • 文件选项: filesincludeexclude
  • 编译选项: compilerOptions
  • 项目引用: extendsreferences

文件选项

files

files指定一个包含相对或绝对文件路径的列表,列举在files中的所有文件,编译器在编译时都会将它们包含在内。

// tsconfig.json
"files": [
"src/core.ts",
"src/index.ts",
]

当配置文件中的files字段值如上所示时,使用tsc命令编译时,src目录下的core.tsindex.ts文件会被编译为core.jsindex.js文件。

include

include指定编译的文件或目录,include是支持使用通配符来匹配路径名,支持的通配符及其功能如下表所示:

通配符功能
*匹配0或多个字符,但是不匹配"."开头的隐藏文件和目录分隔符(/ or \ )
?匹配一个任意字符,但是不匹配目录分隔符
**匹配0或多个字符,包括斜杠(这意味着可以匹配多个目录)

例如,编译src下所有文件:

"include": [
"src",
]

只编译src二级目录下的文件:

"include": [
"src/*/*",
]

tsconfig.json文件同时配置了filesinclude 字段时,编译器会将它们进行合并。当filesinclude中配置的的文件所引用的文件不在其中时,被引用的文件也会被包含进来。

exclude

exclude指定编译时排除的文件或目录,exclude的用法与include相同。

当没有配置filesinclude 字段时,编译器会默认编译当前目录和子目录下所有的ts文件(.ts, .d.ts.tsx),排除在exclude里配置的文件。而当设置了filesinclude 字段时,可以通过exclude字段过滤include的文件。

caution

需要注意的是:通过 files属性明确指定的文件却总是会被编译,不管exclude如何配置

此外,exclude默认情况下会排除node_modulesbower_componentsjspm_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。其中es6js模块化规范,commonjsnode.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运行,代码将编译成功。

note

原因:虽然TSJS的超集,但是仅仅是相对于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
note

js 工程升级为 ts 工程时,在将所有 js 结尾的文件改成 ts 结尾的同时要注意 ts 文件引用 js 文件的情形。此时引用的 js 文件也需要编译导出。

checkJs

checkJs默认值为false,当值为true时允许在js文件中进行类型检查和报错。但checkJs需要配合allowJs一起使用,当allowJsfalse时,checkJs即使为true也不会起作用。

例如当没有配置outDirallowJs的值为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

declarationtrue时,编译后会为每个 ts 文件生成对应的声明文件(.d.ts 文件) 。

原目录:

|-- src
|-- index.ts

编译后目录:

|-- dist
|-- index.js
|-- index.d.ts <= 自动生成的类型声明文件
|-- src
|-- index.ts

声明文件默认会和编译后的js文件保存在同一目录,但是我们可以在compilerOptions中配置declarationDir属性指定声明文件的输出目录。

当我们只想编译生成.d.ts文件而不想编译生成js文件时,可以在compilerOptions中配置 emitDeclarationOnlytrue

caution

在项目中使用工程引用时,必须在根 tsconfig.json 中配置declaration

sourceMap

sourceMaptrue时,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

noEmittrue时,使用 tsc 编译后不会输出文件。

与之相关的属性有noEmitOnErrornoEmitOnErrortrue时,在编译发生错误时不会输出文件。

importHelpers

ts中使用类继承等操作时,编译后的js文件需要用到一些公共方法(helper),每个文件中用到都重新定义一遍是很浪费且很占用空间的。所以当我们配置importHelperstrue时,编译后的文件将不再生成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 __());
};
})();

配置importHelperstrue后,编译后的代码会引用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 时,downlevelIterationtrue会降级实现遍历器。

例如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);
}

downlevelIterationtrue时,编译结果使用了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.tsexp1.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的子集。

strictfalse时,可以根据自己的需求,通过设置其它几项自定义类型检查。

noImplicitAny

noImplicitAny的值为true时,ts文件中所有的函数参数都必须明确参数类型,否则会编译出错。

function echo(o) {
// <= 编译报错:Parameter 'o' implicitly has an 'any' type.
console.log(o);
}

strictNullChecks

strictNullChecks的值为true时,ts文件中不能将nullundefined 赋值给其他类型变量。当赋值给其它类型时会编译出错。

strictFunctionTypes

strictFunctionTypes的值为true时,ts文件不允许函数参数双向协变。

strictBindCallApply

strictBindCallApply的值为true时对 bindcallapply 进行更加严格的类型检查,要求参数类型和个数必须保持一致。

例如当传递的参数少于函数参数,或参数类型不对时,编译时就会报错,如下例所示:

function foo(a: number, b: string): string {
return a + b;
}

let a = foo.call(undefined, 10); // <=== Expected 3 arguments, but got 2
let b = foo.call(undefined, 10, 2); // <=== Argument of type '2' is not assignable to parameter of type 'string'

strictPropertyInitialization

strictPropertyInitialization的值为true时,类的实例属性必须初始化。即类中每个实例属性都有初始值,初始值可以在 constructor 中设置,也可以在声明时设置。

未赋值:

class C {
name: string; // <= Property 'name' has no initializer and is not definitely assigned in the constructor.
}

赋值:

class C {
name: string = 'dk'; // <=== 在声明变量时初始化值
age: number;

constructor(age: number) {
this.age = age; // <=== 在构造函数中赋值
}
}

noImplicitThis

noImplicitThis的值为true时,不允许 this 有隐式的 any 类型,避免this指向全局。

class Age {
age: number = 10;
getAge() {
return function () {
console.log(this.age);
};
}
}

let age = new Age().getAge();

age();

由上例可知this并不指向Age类,而是指向undefined,此时如果noImplicitThis的值为true,将会编译出错。

alwaysStrict

alwaysStrict为 true 时,编译后的代码会在文件开头加上"use strict"

Additional Checks

noUnusedLocals

noUnusedLocals的值为true时,ts文件不允许出现只声明未使用的局部变量。

noUnusedParameters

noUnusedParameters的值为true时,函数中的参数必须在函数中被使用。

由下例可知,参数c在函数参数中被声明,但是并未使用,编译出错

function sum(a: number, b: number, c: number) {
// <= 编译报错:'c' is declared but its value is never read.
return a + b;
}

noImplicitReturns

noImplicitReturns的值为true时,所有的分支必须有返回值。

function fn(a: number) {
if (a > 0) {
return false;
} else {
a = -a; // <= 编译报错:Not all code paths return a value.
}
}

由上例可知,else分支没有返回值,此时只要加上return语句便可编译通过。

noFallthroughCasesInSwitch

noFallthroughCasesInSwitch的值为true时,可以防止switch语句贯穿(当没有break的时候会一直向下执行)。当break丢失时,会编译报错:Fallthrough case in switch

Module Resolution Options

moduleResolution

moduleResolution表示模块解析策略,它的默认值为node,除了使用node对模块进行解析外,还可以使用classic(TypeScript pre-1.6)。

class模块的解析规则:

D3twOQ

node模块的解析规则:

D3twOQ

baseUrl

baseUrl解析非相对导入模块的基地址,默认为当前目录。

paths

paths是指相对于baseUrl的路径映射,paths依赖于baseUrl而存在。

比如,当index.ts想导入Jquery的精简版本,而不是默认版本。可以设置path为:

"baseUrl": "./",
"paths": {
"jquery": ["node_modules/jquery/dist/jquery.slim.min.js"]
}

目录结构:

|-- node_modules
|-- index.ts

rootDirs

rootDirs 功能是将多个目录放在一个虚拟目录下。

|-- dist
|--util.d.ts
|--util.js
|-- src
|-- index.ts

util这个类库会一直在输出目录,不会再次被构建。而当src文件下的index.js引用util时,需要使用以下方式:

import { util } from '../dist/util';

但当src被构建进输出目录后,之前导入类库的路径就会出错。此时可以使用 rootDirsdistsrc放在同一个虚拟目录,如下所示:

"rootDirs": ['src', 'dist']

此时util.*index.ts可以看作在同一目录下,所以将引用改为:

import { util } from './util';

typeRoots

typeRoots指的是声明文件的目录,默认node_modules/@types

当我们使用npm安装了node包后,发现引用fs模块依旧出错,这是因为 typescript 不认识 nodejs 原生api,需要安装对应的声明文件 @types/node。与node相似,当我们安装包后,引用依旧出错,可以看一下是否安装了它的声明文件。

types

types为声明文件包,当我们将包名放到types属性中时,只会从types的属性值中进行查找。

例如配置为:

{
"compilerOptions": {
"types" : ["node", "lodash"]
}
}

编译时只会找 ./node_modules/@types/node, ./node_modules/@types/lodash两个包的声明文件,其它包的声明文件不会查找。

esModuleInterop

esModuleInterop的默认值为false,当esModuleInterop的值为true时,允许模块使用export = 模块名导出,由import XX from "模块名"导入。

例如在react项目中,我们项目中会使用import React from 'react';,因此我们需要将esModuleInterop的值设为true。否则,只能使用import * as React from 'react'来引入react

allowUmdGlobalAccess

allowUmdGlobalAccess的值为true时,允许以全局变量的形式访问 UMD 模块

工程引用

工程引用是TypeScript 3.0的新特性,它支持将TypeScript程序的结构分割成更小的组成部分,并可以配置这些小的部分产生依赖关系。这样可以改善构建时间,强制在逻辑上对组件进行分离,更好地组织你的代码。

TypeScript 3.0还引入了tsc的一种新模式,即--build(简写为-b)标记,它与工程引用协同工作可以加速TypeScript的构建。使用方式:

tsc -b 文件路径

工程引用前

工程引用出现前,因为所有的配置都是在同一个文件tsconfig中,配置起来顾此失彼,略显笨重。可以看一下下面这个目录结构:

├── src
│ ├── client
│ │ ├── index.ts
│ ├── common
│ │ ├── index.ts
│ ├── server
│ │ ├── index.ts
├── test
│ ├── client.spec.ts
│ ├── server.spec.ts
├── package.json
├── tsconfig.json

可以将该项目的看作一个前后端未分离的项目,client下是客户端代码,server下是服务器代码,common是两者共用的文件,其中client/index.tsserver/index.ts 会引用common/index.ts的代码。test中存放的是clientserver的单元测试代码。

tsconfig.json的配置如下:

{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"outDir": "./dist"
}
}

项目编译后的dist目录如下:

├── dist
│ ├── src
│ │ ├── client
│ │ ├── common
│ │ ├── server
│ ├── test

通过上面的内容可以看出,在没有引入工程引用前存在以下几个缺点:

  1. 希望 src 下面的文件直接被编译到 dist 目录下, 但由于需要编译test文件而达不到这样的效果。

  2. 我们无法单独构建 client 端, 或者 server 端的代码,构建体积较大。

  3. 我们不希望把test构建到 dist 目录下。

  4. 一个文件发生改动,要编译所有文件,构建比较耗时。

虽然1可以通过配置include字段实现只将src下的文件编译到dist,同时也满足了3,但是这样test不能一起编译会显得很麻烦。此时我们可以通过使用工程引用,单独编译各个模块。

使用工程引用

为了优化构建过程,我们可以使用工程引用,将之前的目录改造如下:

├── src
│ ├── client
│ │ ├── index.ts
│ │ ├── tsconfig.json
│ ├── common
│ │ ├── index.ts
│ │ ├── tsconfig.json
│ ├── server
│ │ ├── index.ts
│ │ ├── tsconfig.json
├── test
│ ├── client.spec.ts
│ ├── server.spec.ts
│ ├── tsconfig.json
├── package.json
├── tsconfig.json

通过目录结构可以看出,我们为每个目录都添加了tsconfig.json来满足各个模块的编译需求。

tsconfig.json 文件配置如下:

{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
// "outDir": "./dist" 关闭 outDir 选项, 即不在根配置中指定输入目录
"composite": true, // 使用 composite, 它意味着工程可以被引用, 可以帮助ts编译器快速定位工程文件的输出文件
"declaration": true // 使用 composite 选项必须开启 declaration
}
}

使用工程引用时,client/tsconfig.json的配置:

{
"extends": "../../tsconfig.json", // 导入根配置文件
"compilerOptions": {
"outDir": "../../dist/client" // 指定输出目录
},
"references": [{ "path": "../common" }] // 因为 client 引用了 common, 故需要将 common 引入进来
}

其中extends用来导入根配置文件。而references导入引用工程中的模块实际加载的是它输出的声明文件.d.ts),这也是在根配置文件中必须配置"composite": true"declaration": true的原因。

在使用tsc -b src/client进行编译时,它所依赖的common也会被编译到dist文件,其目录结构如下所示:

├─dist
│ ├─client
│ │ index.d.ts
│ │ index.js
│ │ tsconfig.tsbuildinfo
│ │
│ └─common
│ index.d.ts
│ index.js
│ tsconfig.tsbuildinfo

server/tsconfig.json的配置与client/tsconfig.json类似,只需要将outDir改动以下即可。当server编译时,由于common文件已经被编译过了,所以此时不会对其再次编译,这样也节省了编译的时间。

common/tsconfig.json的配置:

{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/common"
}
}

test/tsconfig.json的配置:

{
"extends": "../tsconfig.json", // 导入根配置文件
"references": [{ "path": "../src/client" }, { "path": "../src/server" }] // 引用工程文件
}

tsc -b 命令行

上面也提到,使用tsc -b可以对ts文件进行编译。

> tsc -b                                # Build the tsconfig.json in the current directory
> tsc -b src # Build src/tsconfig.json
> tsc -b foo/release.tsconfig.json bar # Build foo/release.tsconfig.json and bar/tsconfig.json

tsc -b 还支持其它一些选项:

  • --verbose:打印详细的日志(可以与其它标记一起使用)
  • --dry: 显示将要执行的操作但是并不真正进行这些操作
  • --clean: 删除指定工程的输出(可以与--dry 一起使用)
  • --force: 把所有工程当作非最新版本对待
  • --watch: 观察模式(可以与--verbose 一起使用)

--verbose

对于前面提到的项目,在编译clientserver时就可以添加--verbose参数,打印编译日志。具体过程如下所示:

  • tsc -b ./src/client --verbose

build client 日志:

[11:49:51] Project 'src/common/tsconfig.json' is out of date because output file 'dist/common/index.js' does not exist
[11:49:51] Building project 'D:/Code/ts/src/new/src/common/tsconfig.json'...
[11:49:56] Project 'src/client/tsconfig.json' is out of date because output file 'dist/client/index.js' does not exist
[11:49:56] Building project 'D:/Code/ts/src/new/src/client/tsconfig.json'...
  • tsc -b ./src/server --verbose

build server 日志:

[11:50:09] Project 'src/common/tsconfig.json' is up to date because newest input 'src/common/index.ts' is older than oldest output 'dist/common/index.js'
[11:50:09] Project 'src/server/tsconfig.json' is out of date because output file 'dist/server/index.js' does not exist
[11:50:09] Building project 'D:/Code/ts/src/new/src/server/tsconfig.json'...

通过打印的日志可以看出common只编译了一次,不会被重复编译。

--dry

server未编译时,执行 tsc -b ./src/server --verbose --dry时的输出日志如下所示:

[12:52:45] Project 'src/common/tsconfig.json' is up to date because newest input 'src/common/index.ts' is older than oldest output 'dist/common/index.js'
[12:52:45] Project 'D:/Code/ts/src/new/src/common/tsconfig.json' is up to date
[12:52:45] Project 'src/server/tsconfig.json' is out of date because output file 'dist/server/index.js' does not exist
[12:52:45] A non-dry build would build project 'D:/Code/ts/src/new/src/server/tsconfig.json'

server编译后,再次执行 tsc -b ./src/server --verbose --dry时的输出日志如下所示:

[12:55:24] Project 'src/common/tsconfig.json' is up to date because newest input 'src/common/index.ts' is older than oldest output 'dist/common/index.js'
[12:55:24] Project 'D:/Code/ts/src/new/src/common/tsconfig.json' is up to date
[12:55:24] Project 'src/server/tsconfig.json' is up to date because newest input 'src/server/index.ts' is older than oldest output 'dist/server/index.js'
[12:55:24] Project 'D:/Code/ts/src/new/src/server/tsconfig.json' is up to date

通过上面的日志可以看出,当使用--dry参数时会检测将要执行的操作,但并不会真正的执行。

--clean

使用--clean参数时会删除指定工程的输出。例如:

  • tsc -b ./src/server --dry --clean
A non-dry build would delete the following files:
* D:/Code/ts/src/new/dist/common/index.js
* D:/Code/ts/src/new/dist/common/index.d.ts
* D:/Code/ts/src/new/dist/common/tsconfig.tsbuildinfo
* D:/Code/ts/src/new/dist/server/index.js
* D:/Code/ts/src/new/dist/server/index.d.ts
* D:/Code/ts/src/new/dist/server/tsconfig.tsbuildinfo

由输出结果可以看出,通过添加--clean,清除已经编译好的文件,即tsc -b ./src/server --cleantsc -b ./src/server的逆操作。

--force

如果想刷新已经编译好的文件,可以使用 --force参数。下面对已编译过的server为例:

  • tsc -b ./src/server --verbose
[12:24:44] Project 'src/common/tsconfig.json' is up to date because newest input 'src/common/index.ts' is older than oldest output 'dist/common/index.js'
[12:24:44] Project 'src/server/tsconfig.json' is up to date because newest input 'src/server/index.ts' is older than oldest output 'dist/server/index.js'
  • tsc -b ./src/server --verbose --force
[12:19:51] Project 'src/common/tsconfig.json' is up to date because newest input 'src/common/index.ts' is older than oldest output 'dist/common/index.js'
[12:19:51] Building project 'D:/Code/ts/src/new/src/common/tsconfig.json'...
[12:19:57] Project 'src/server/tsconfig.json' is up to date with .d.ts files from its dependencies
[12:19:57] Building project 'D:/Code/ts/src/new/src/server/tsconfig.json'...

--watch

当使用--watch参数时会开启观察编译模式,当检测到被观察的文件出现修改后,将启动增量编译。

  • tsc -b ./src/server --verbose --watch
[12:28:50] Starting compilation in watch mode...
[12:28:50] Project 'src/common/tsconfig.json' is up to date because newest input 'src/common/index.ts' is older than oldest output 'dist/common/index.js'
[12:28:50] Project 'src/server/tsconfig.json' is up to date because newest input 'src/server/index.ts' is older than oldest output 'dist/server/index.js'
[12:28:50] Found 0 errors. Watching for file changes.
  • 修改server/index.ts文件后:
[12:30:55] File change detected. Starting incremental compilation...
[12:30:55] Project 'src/server/tsconfig.json' is out of date because oldest output 'dist/server/index.js' is older than newest input 'src/server/index.ts'
[12:30:55] Building project 'D:/Code/ts/src/new/src/server/tsconfig.json'...
[12:30:57] Found 0 errors. Watching for file changes.

由修改后打印的日志可以看出,文件修改后会对修改的文件进行增量编译,然后继续观察文件。

参考目录

  1. 配置 tsconfig.json:文件选项,by 梁宵
  2. 配置 tsconfig.json:编译选项,by 梁宵
  3. tsconfig.json 详解一,by dkvirus
  4. TypeScript Deep Dive,by Basarat
  5. TypeScript official docs: Compiler Options
  6. TypeScript official docs: tsconfig.json
  7. 配置 tsconfig.json:工程引用,by 梁宵
  8. 聊一聊 TypeScript 的工程引用, by YanceyOfficial
  9. TypeScript official docs: Project References