快速上手,tsconfig (编译选项)

2,029
{
  "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 解决。
    "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 语句贯穿,分支没有 break
    "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

tsBuildInfoFile

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

diagnostics

打印编译信息。

组合使用

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

第一次编译信息:

Files:              113
Lines:            85109
Nodes:           263776
Identifiers:      97483
Symbols:          78297
Types:            26645
Instantiations:   20354
Memory used:    149278K
I/O read:         0.06s
I/O write:        0.00s
Parse time:       1.23s
Bind time:        0.58s
Check time:       2.17s
Emit time:        0.04s
Total time:       4.02s

第二次编译信息:

Files:             113
Lines:           85109
Nodes:          263776
Identifiers:     97483
Symbols:         59089
Types:              78
Instantiations:      0
Memory used:    99709K
I/O read:        0.09s
I/O write:       0.00s
Parse time:      1.58s
Bind time:       0.64s
Check time:      0.00s
Emit time:       0.00s
Total time:      2.22s

Total Time 可以相差 50%左右。

目标语言和模块

target

设置目标语言的版本,可设置为 ES3、ES5 和 ES2015 等等,默认为 ES3。

module

设置生成代码的模块标准,可以设置为 CommonJS、AMD 和 UMD 等等

outFile

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

组合使用

我们创建两个文件:

// ./src/amd.ts

let amd: string[] = ["a"];
export = amd;
// ./src/index.ts
import a = require("./amd");

let hello: string = "Hello TypeScript";
document.querySelectorAll(".app")[0].innerHTML = hello;
{
  "compilerOptions": {
    "module": "amd",
    "outFile": "./app.js"
  }
}

编译后得到一个 app.js 文件:

define("amd", ["require", "exports"], function (require, exports) {
  "use strict";
  var amd = ["a"];
  return amd;
});
define("index", ["require", "exports"], function (require, exports) {
  "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  var hello = "Hello TypeScript";
  document.querySelectorAll(".app")[0].innerHTML = hello;
});
var libs = {};

类库 lib

指定 ts 需要引用的库,即声明文件。

如果 target: es5,默认导入类库 ["dom", "es5", "scripthost"]

举例:如果要用到 es2019 的方法:

let array = [1, 2, 3, [4, 5]].flat();
{
  "compilerOptions": {
    "lib": ["ES2019.Array"]
  }
}

编译 JS 文件

allowJS

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

checkJS

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

outDir

指定输出目录。

rootDir

指定输入文件目录(用于输出)。

组合使用

{
  "compilerOptions": {
    "allowJS": true,
    "checkJS": true
  }
}
// ERROR! Cannot write file '/Users/xqq/workspace/qq/code/geekUniversity/TypeScript/ts_in_action/build/webpack.base.config.js' because it would overwrite input file.

报错:编译器会默认编译所有的 js 文件,包括 build 下的 webpack 文件。我们需要 include,并且编译后的文件会覆盖原文件,所以还需要指定输出目录。

{
  "include": "./src",
  "compilerOptions": {
    "allowJS": true,
    "checkJS": true,
    "outDir": "./out"
  }
}

编译后文件目录

|-- out
    |-- a.js
    |-- index.js
|-- src
    |-- a.js
    |-- index.ts

如果指定 rootDir: './' ,得到的目录结构:

|-- out
    |-- src
        |-- a.js
        |-- index.js

声明文件 & sourceMap

declaration

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

declarationDir

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

emitDeclarationOnly

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

sourceMap

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

inlineSourceMap

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

declarationMap

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

typeRoots

设置声明文件目录,默认 node_modules/@types > types, 这是声明文件包,如果设置了某一个声明文件,那么编译器只会加载这个声明文件。

组合使用

  • 生成声明文件

    {
      "declaration": true, // 生成声明文件
      "declarationDir": "./d", // 声明文件的路径
      "emitDeclarationOnly": true // 只生成声明文件
    }
    

    编译后,只在 ./d 文件夹下生成 index.d.ts 声明文件。

  • sourceMap 文件

    {
      "sourceMap": true
    }
    

    编译后,生成 index.jsindex.js.map

  • inlineSourceMap 文件

    {
      "inlineSourceMap": true
    }
    

    编译后,生成 index.js

    // import a = require("./amd");
    var hello = "Hello TypeScript";
    document.querySelectorAll(".app")[0].innerHTML = hello;
    // let array = [1, 2, 3, [4, 5]].flat(); // 扁平化数组 [1,2,3,4,5]
    //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQkFBK0I7QUFFL0IsSUFBSSxLQUFLLEdBQVcsa0JBQWtCLENBQUM7QUFDdkMsUUFBUSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsR0FBRyxLQUFLLENBQUM7QUFFdkQsNkRBQTZEIn0=
    
  • 声明文件的 sourceMap

    {
      "declaration": true,
      "declarationMap": true
    }
    

    编译后,生成 index.jsindex.d.tsindex.d.ts.map

helper 函数

noEmitHelpers

设置为 true 时,不生成 helper 函数。

举例子:

class A {}
class B extends A {}
export = A;

编译后:

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

downlevelIteration

之前迭代器只在编译目标为 ES6/ES2015 或者更新版本时可用. 此外, 设计迭代器协议的结构, 比如 for..of, 如果编译目标低于 ES6/ES2015, 则只能在操作数组时被支持.

TypeScript 2.3ES3ES5 为编译目标时由 --downlevelIteration 选项增加了完整的对生成器和迭代器协议的支持。

通过 --downlevelIteration 选项, 编译器会使用新的类型检查和输出行为, 尝试调用被迭代对象的 [Symbol.iterator]() 方法 (如果有), 或者在对象上创建一个语义上的数组迭代器.

注意这需要非数组的值有原生的 Symbol.iterator 或者 Symbol.iterator 的运行时模拟实现.

使用 --downlevelIteration 时, 在 ES5/ES3for..of 语句, 数组解构, 数组中的元素展开, 函数调用, new 表达式在支持 Symbol.iterator 时可用, 但即便没有定义 Symbol.iterator, 它们在运行时或开发时都可以被使用到数组上.

// index.ts
let a = [1, 2, 3];
let b = [1, ...a];
// downlevelIteration: false
var __spreadArray =
  (this && this.__spreadArray) ||
  function (to, from) {
    for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
      to[j] = from[i];
    return to;
  };
var a = [1, 2, 3];
var b = __spreadArray([1], a);
// downlevelIteration: true
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 __spreadArray =
  (this && this.__spreadArray) ||
  function (to, from) {
    for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
      to[j] = from[i];
    return to;
  };
var a = [1, 2, 3];
var b = __spreadArray([1], __read(a));

严格模式

strict

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

strictNullChecks

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

// strictNullChecks: false
let a: number = 1;
a = null; // OK
a = undefined; // OK

strictFunctionTypes

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

// strictFunctionTypes: false
interface point2D {
  x: number;
  y: number;
}
interface point3D {
  x: number;
  y: number;
  z: number;
}
let p2d = (value: point2D) => {};
let p3d = (value: point3D) => {};

p3d = p2d;
p2d = p3d; // OK

strictBindCallApply

严格的 bindcallapply 检查。

// strictvindCallApply: true
function add(a: number, b: number) {
  return a + b;
}
add.call(undefined, 1, "2"); // Error!
// strictvindCallApply: false
function add(a: number, b: number) {
  return a + b;
}
add.call(undefined, 1, "2"); // OK!

noImplicitThis

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

// noImplicitThis: true
class A {
  a: number = 1;
  getA() {
    return function () {
      console.log(this.a); // ERROR
    };
  }
}
new A().getA()(); // Uncaught TypeError: Cannot read property 'a' of undefined
class A {
  a: number = 1;
  getA() {
    return () => {
      console.log(this.a); // OK
    };
  }
}

模块

esModuleInterop

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

// a.ts
let a: number = 1;
export = a;
// esModuleInterop: false
import a from "./a";
// ERROR: 必须要用 import a require('./a')

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
       *
       * 依次向上目录查找
       *
       * /root/node_modules/moduleB.ts
       * /root/node_modules/moduleB.tsx
       * /root/node_modules/moduleB.d.ts
       * /root/node_modules/package.json ( “types” 属性)
       * /root/node_modules/index.ts
       * /root/node_modules/index.tsx
       * /root/node_modules/index.d.ts
       *
       * /node_modules/moduleB.ts
       * /node_modules/moduleB.tsx
       * /node_modules/moduleB.d.ts
       * /node_modules/package.json ( “types” 属性)
       * /node_modules/index.ts
       * /node_modules/index.tsx
       * /node_modules/index.d.ts
       */
      

baseUrl

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

paths

路径映射,相对于 baseUrl。比如想引入 jquery 的精简版,可以制定他的相对路径。

{
  "compilerOptions": {
    "paths": {
      // 路径映射,相对于 baseUrl
      "jquery": ["node_modules/jquery/dist/jquery.slim.min.js"]
    }
  }
}

rootDirs(区别于 rootDir)

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

// ./tsconfig.json
{
  "compilerOptions": {
    "rootDirs": ["src", "out"]
  }
}

新建 out 目录,用于存放构建之后的文件。

// ./out/util.ts
let a: number = 1;
export = a;

构建之后,index 也会被放入 out 目录。为了编译之后不需要重新修改路径,我们可以提前虚拟他们在一个目录下。

// ./src/index.ts
import a require("./util")

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

TypeScript 工程系列