一起學 TypeScript 配置篇

D文斌發表於2020-03-30

本篇主要介紹 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開發教程收藏並閱讀.

相關文章