一起学 TypeScript 配置篇

8,025 阅读11分钟

本篇主要介绍 TypeScript 中的 tsconfig.json 文件的作用以及配置参数.主要介绍文件选项和编译选项两部分.

如果一个目录下存在一个 tsconfig.json 文件,那么它意味着这个目录是 TypeScript 项目的根目录。tsconfig.json 文件中指定了用来编译这个项目的根文件和编译选项。 一个项目可以通过以下方式之一来编译:

  • 不带任何输入文件的情况下调用 tsc,编译器会从当前目录开始去查找 tsconfig.json文 件,逐级向上搜索父目录。
  • 不带任何输入文件的情况下调用 tsc,且使用命令行参数 --project(或 -p )指定一个包含 tsconfig.json 文件的目录。

当命令行上指定了输入文件时,tsconfig.json文件会被忽略。

文件选项

files

它的含义是编译器需要编译的相对或绝对文件路径的单个文件列表。

{
  "files": [
    "src/index.ts"
  ]
}

这时执行 tsc 命令,编译器会编译 src/index.ts 文件。

include

它的含义是编译器需要编译的文件或者目录。

{
  "include": [
    "src"
  ]
}

这时执行 tsc 命令,编译器会编译 src 目录下的所有 ts 文件。

exclude

它的含义是编译器需要排除的文件或者目录。默认会排除 node_modules 目录下的所有文件。

{
  "exclude": [
    "src/lib"
  ]
}
    1. 如果 filesincludes 都没有指定,编译器默认包含当前目录下所有的 ts 文件。(.ts.d.ts.tsx
    1. 如果 exclude 存在,exclude 配置优先级高于 filesincludes 配置
    1. excludeincludes 配置支持 glob 通配符:*?**

extends

我们可以把一些配置抽离出一个配置文件,再 tsconfig.json 文件引入,方便以后管理与维护。

// tsconfig.json

{
  "extends": "./base.json"
}

在主配置文件中,设置文件选项会覆盖调继承文件中的相同的配置项。

compileOnSave

它可以让 IDE 在保存文件时,编译器自动编译。

{
  "compileOnSave": true
}

目前只有个别 IDE 支持。

编译选项

大致配置如下所示:

{
  "compilerOptions": {
    "incremental": true,                // 增量编译
    "tsBuildInfoFile": "./buildFile",   // 增量编译文件的存储位置
    "diagnostics": true,                // 打印编译信息

    "target": "es5",           // 目标语言的版本
    "module": "commonjs",      // 生成代码的模块标准
    "outFile": "./app.js",     // 将多个相互依赖的文件生成一个文件,可以用在 AMD 模块中

    "lib": [],                 // TS 需要引用的库,即声明文件,es5 默认 "dom", "es5", "scripthost"

    "allowJs": true,           // 允许编译 JS 文件(js、jsx)
    "checkJs": true,           // 允许在 JS 文件中报错,通常与 allowJS 一起使用
    "outDir": "./out",         // 指定输出目录
    "rootDir": "./",           // 指定输入文件目录(用于输出)

    "declaration": true,         // 生成声明文件
    "declarationDir": "./d",     // 声明文件的路径
    "emitDeclarationOnly": true, // 只生成声明文件
    "sourceMap": true,           // 生成目标文件的 sourceMap
    "inlineSourceMap": true,     // 生成目标文件的 inline sourceMap
    "declarationMap": true,      // 生成声明文件的 sourceMap
    "typeRoots": [],             // 声明文件目录,默认 node_modules/@types
    "types": [],                 // 声明文件包

    "removeComments": true,    // 删除注释

    "noEmit": true,            // 不输出文件
    "noEmitOnError": true,     // 发生错误时不输出文件

    "noEmitHelpers": true,     // 不生成 helper 函数,需额外安装 ts-helpers
    "importHelpers": true,     // 通过 tslib 引入 helper 函数,文件必须是模块

    "downlevelIteration": true,    // 降级遍历器的实现(es3/5)

    "strict": true,                        // 开启所有严格的类型检查
    "alwaysStrict": false,                 // 在代码中注入 "use strict";
    "noImplicitAny": false,                // 不允许隐式的 any 类型
    "strictNullChecks": false,             // 不允许把 null、undefined 赋值给其他类型变量
    "strictFunctionTypes": false,          // 不允许函数参数双向协变
    "strictPropertyInitialization": false, // 类的实例属性必须初始化
    "strictBindCallApply": false,          // 严格的 bind/call/apply 检查
    "noImplicitThis": false,               // 不允许 this 有隐式的 any 类型

    "noUnusedLocals": true,                // 检查只声明,未使用的局部变量
    "noUnusedParameters": true,            // 检查未使用的函数参数
    "noFallthroughCasesInSwitch": true,    // 防止 switch 语句贯穿
    "noImplicitReturns": true,             // 每个分支都要有返回值

    "esModuleInterop": true,               // 允许 export = 导出,由import from 导入
    "allowUmdGlobalAccess": true,          // 允许在模块中访问 UMD 全局变量
    "moduleResolution": "node",            // 模块解析策略
    "baseUrl": "./",                       // 解析非相对模块的基地址
    "paths": {                             // 路径映射,相对于 baseUrl
      "jquery": ["node_modules/jquery/dist/jquery.slim.min.js"]
    },
    "rootDirs": ["src", "util"],           // 将多个目录放在一个虚拟目录下,用于运行时

    "listEmittedFiles": true,        // 打印输出的文件
    "listFiles": true,               // 打印编译的文件(包括引用的声明文件)
  }
}

接下来,我们会逐个分析上面的配置项。

incremental

它的含义是增量编译,TypeScript 编译器在第一次编译后会生成一个可以编译信息的文件,在之后的编译之后会根据这个文件提高编译的速度。该文件默认会在根目录下名称为 tsconfig.tsbuildinfo

{
  "program": {
    "fileInfos": {
      "../../../../../usr/local/lib/node_modules/typescript/lib/lib.d.ts": {
        "version": "49ff9798f592c8b7e628fd881401e68810c1b3589ecd7a41b32b3c287374cde0",
        "signature": "49ff9798f592c8b7e628fd881401e68810c1b3589ecd7a41b32b3c287374cde0"
      },
      "../../../../../usr/local/lib/node_modules/typescript/lib/lib.es5.d.ts": {
        "version": "ff5688d6b2fcfef06842a395d7ff4d5730d45b724d4c48913118c889829052a1",
        "signature": "ff5688d6b2fcfef06842a395d7ff4d5730d45b724d4c48913118c889829052a1"
      },
      "../../../../../usr/local/lib/node_modules/typescript/lib/lib.dom.d.ts": {
        "version": "2d53f3741e5a4f78a90f623387d71a1cc809bb258f10cdaec034b67cbf71022f",
        "signature": "2d53f3741e5a4f78a90f623387d71a1cc809bb258f10cdaec034b67cbf71022f"
      },
      "../../../../../usr/local/lib/node_modules/typescript/lib/lib.webworker.importscripts.d.ts": {
        "version": "fe4e59403e34c7ff747abe4ff6abbc7718229556d7c1a5b93473fb53156c913b",
        "signature": "fe4e59403e34c7ff747abe4ff6abbc7718229556d7c1a5b93473fb53156c913b"
      },
      "../../../../../usr/local/lib/node_modules/typescript/lib/lib.scripthost.d.ts": {
        "version": "b9faa17292f17d2ad75e34fac77dd63a6403af1dba02d39cd0cbb9ffdf3de8b9",
        "signature": "b9faa17292f17d2ad75e34fac77dd63a6403af1dba02d39cd0cbb9ffdf3de8b9"
      },
      "./src/index.ts": {
        "version": "a0e2a405f15ab7f6218e22c622acc2706d51eae2aa90f302f81f68628e22cd55",
        "signature": "ec8f4696ee1308e5fbc9f50626f5677f0f15bd7c228311cbcc0669233461fa1d"
      }
    },
    "options": {
      "incremental": true,
      "configFilePath": "./tsconfig.json"
    },
    "referencedMap": {},
    "exportedModulesMap": {},
    "semanticDiagnosticsPerFile": [
      "../../../../../usr/local/lib/node_modules/typescript/lib/lib.d.ts",
      "./src/index.ts",
      "../../../../../usr/local/lib/node_modules/typescript/lib/lib.es5.d.ts",
      "../../../../../usr/local/lib/node_modules/typescript/lib/lib.dom.d.ts",
      "../../../../../usr/local/lib/node_modules/typescript/lib/lib.webworker.importscripts.d.ts",
      "../../../../../usr/local/lib/node_modules/typescript/lib/lib.scripthost.d.ts"
    ]
  },
  "version": "3.6.2"
}

tsBuildInfoFile

可以修改增量编译文件的存储文件夹和文件名

diagnostics

打印编译信息。

Files:            6
Lines:        24817
Nodes:       111372
Identifiers:  41045
Symbols:      27914
Types:         8268
Memory used: 68338K
I/O read:     0.01s
I/O write:    0.00s
Parse time:   0.42s
Bind time:    0.23s
Check time:   1.13s
Emit time:    0.02s
Total time:   1.80s

target

设置目标语言的版本,可设置为 ES3ES5ES2015 等等,默认为 ES3

module

设置生成代码的模块标准,可以设置为 CommonJSAMDUMD 等等。

outFile

将多个相互依赖的文件生成一个文件,可以用在 AMD 模块中。

我们创建两个文件,分别为 index.tsamd.ts,如下所示:

// ./src/index.ts
import a = require('./amd')

let str: string = 'abc'
// ./src/amd.ts

let amd: number = 0

export = amd

index.ts 引入 amd.ts,我们再设置一下 tsconfig.json 文件。

{
  "compilerOptions": {
    "module": "amd",
    "outFile": "./app.js"
  }
}

然后在命令行执行 tsc 命令,编译器会将两个 ts 文件合并编译成一个 app.js 文件。

define("amd", ["require", "exports"], function (require, exports) {
    "use strict";
    var amd = 0;
    return amd;
});
define("index", ["require", "exports"], function (require, exports) {
    "use strict";
    exports.__esModule = true;
    var str = 'abc';
});

lib

指定 ts 需要引用的库,即声明文件,若 target 设置为 es5 时,lib 默认为 ["dom", "es5", "scripthost"]

例如,我们想在 ts 中使用 es2019 的方法。可以在 lib 配置里添加 es2019

allowJs

允许编译器编译 JS 文件(js、jsx)。

checkJs

允许在 JS 文件中报错,通常与 allowJS 一起使用。

outDir

指定输出目录

rootDir

指定输入文件目录

declaration

编译器编译时,允许生成声明文件(.d.ts)。

declarationDir

指定声明文件的生成的目录。

emitDeclarationOnly

编译器编译时,只允许生成声明文件。

sourceMap

编译器编译时,生成目标文件的 sourceMap 文件。

inlineSourceMap

编译器编译时,将 sourceMap 生成在 js 文件中。

declarationMap

编译器编译时,生成声明文件的 sourceMap。

typeRoots

设置声明文件目录,默认 node_modules/@types

types

这是声明文件包,如果设置了某一个声明文件,那么编译器只会加载这个声明文件。

removeComments

是否删除注释

noEmit

执行 tsc 不会输出任何文件

noEmitOnError

发生错误时不输出文件

noEmitHelpers

设置为 true 时,不生成 helper 函数。先看下面示例:

class B {}

class A extends B {}

export = A

我们创建了一个模块。然后在控制台执行 tsc,下面就是编译后的结果:

"use strict";
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 __());
    };
})();
var B = /** @class */ (function () {
    function B() {
    }
    return B;
}());
var A = /** @class */ (function (_super) {
    __extends(A, _super);
    function A() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return A;
}(B));
module.exports = A;

编译器会自动生成 __extends

如果我们将 noEmitHelpers 这个配置设置为 true 之后。编译后的结果如下:

"use strict";
var B = /** @class */ (function () {
    function B() {
    }
    return B;
}());
var A = /** @class */ (function (_super) {
    __extends(A, _super);
    function A() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return A;
}(B));
module.exports = A;

上面的编译后的结果中 __extends 未定义。ts 已经为开发者定义了一个配置项,方便解决该问题。 就是接下来要介绍的配置 importHelpers

importHelpers

通过 tslib 引入 helper 函数,文件必须是模块。编译结果如下:

"use strict";
var tslib_1 = require("tslib");
var B = /** @class */ (function () {
    function B() {
    }
    return B;
}());
var A = /** @class */ (function (_super) {
    tslib_1.__extends(A, _super);
    function A() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return A;
}(B));
module.exports = A;

若提示 tslib 未找到时,可以手动安装它。

downlevelIteration

降级遍历器的实现,下面是一个 es6 语法:

let a = [1, 2, 3]
let b = [4, ...a]

我们打开这项配置,进行编译后结果如下:

var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spread = (this && this.__spread) || function () {
    for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
    return ar;
};
var a = [1, 2, 3];
var b = __spread([4], a);

会生成两个 helper 函数。

strict

表示开启所有严格的类型检查,若 strict 为 true,alwaysStrictnoImplicitAnystrictNullChecksstrictFunctionTypesstrictPropertyInitializationstrictBindCallApplynoImplicitThis 选项默认都为 true。

alwaysStrict

在代码中注入 use strict

noImplicitAny

不允许隐式的 any 类型。

strictNullChecks

不允许把 nullundefined 赋值给其他类型变量。

strictFunctionTypes

不允许函数参数双向协变。

strictPropertyInitialization

类的实例属性必须初始化。

strictBindCallApply

严格的 bindcallapply 检查。

function add (a: number, b: number) {
  return a + b
}

add.call(undefined, 1, '2')
// Error: Argument of type '"2"' is not assignable to parameter of type 'number'.

noImplicitThis

不允许 this 有隐式的 any 类型。

class A {
  name: string = 'abc'
  getName () {
    return function () {
      console.log(this.name)
    }
  }
}
// Error: 'this' implicitly has type 'any' because it does not have a type annotation.

noUnusedLocals

检查只声明,未使用的局部变量

noUnusedParameters

检查未使用的函数参数

noFallthroughCasesInSwitch

防止 switch 语句贯穿

noImplicitReturns

每个分支都要有返回值

esModuleInterop

允许 export = 方式导出,也可以用 import = 的方式导入。

allowUmdGlobalAccess

允许在模块中访问 UMD 全局变量

moduleResolution

模块解析策略,这里提供两种解析策略 nodeclassicts 默认使用 node 解析策略。

  • classic 模块解析策略

适用于 AMDSystemES2015

如果一个模块使用相对方式导入时,ts 就会依次解析同级目录 .ts.d.ts 文件。

// /root/src/moduleA.ts

import { b } from './moduleB'

/**
 * /root/src/moduleB.ts
 * /root/src/moduleB.d.ts
 */

如果使用非相对方式导入时如下, ts 会从当前目录的 node_modules 目录里查找,如果未找到,会依次向上级目录查找。

// /root/src/moduleA.ts

import { b } from 'moduleB'

/**
 * /root/src/node_modules/moduleB.ts
 * /root/src/node_modules/moduleB.d.ts
 * 
 * /root/node_modules/moduleB.ts
 * /root/node_modules/moduleB.d.ts
 * 
 * /node_modules/moduleB.ts
 * /node_modules/moduleB.d.ts
 */
  • node 模块解析策略

使用相对方式导入

// /root/src/moduleA.ts

import { b } from './moduleB'

/**
 * /root/src/moduleB.ts
 * /root/src/moduleB.tsx
 * /root/src/moduleB.d.ts
 * /root/src/moduleB/package.json ( types 属性)
 * /root/src/moduleB/index.ts
 * /root/src/moduleB/index.tsx
 * /root/src/moduleB/index.d.ts
 */

使用非相对方式导入

// /root/src/moduleA.ts

import { b } from 'moduleB'

/**
 * /root/src/node_modules/moduleB.ts
 * /root/src/node_modules/moduleB.tsx
 * /root/src/node_modules/moduleB.d.ts
 * /root/src/node_modules/package.json ( types 属性)
 * /root/src/node_modules/index.ts
 * /root/src/node_modules/index.tsx
 * /root/src/node_modules/index.d.ts
 * 
 * 依次向上目录查找
 */

baseUrl

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

paths

路径映射,相对于 baseUrl。比如示例中我们想引入 jquery 精简版本,可以制定它的相对路径。

rootDirs

将多个目录放在一个虚拟目录下,用于运行时。

比如 我们创建量以下两个文件。

// /util/a.ts
let a: string = 'A'
export = a
// /src/index.ts
import a from './a'

注意在引入 a 时,是引入的当前目录。因为当 rootDirs 设置了 srcutil 目录时,编译器默认它们属于同级目录。

listEmittedFiles

打印输出的文件。

listFiles

打印编译的文件,包括引用的声明文件。

结语

想要了解更多关于 TypeScript 相关知识,可以点击TypeScript开发教程收藏并阅读.