tsconfig常用設定全解

2022-07-19 06:00:43

基於typescript的專案的根目錄下都會有一個檔案(tsconfig.json), 這個檔案主要用來控制typescript編譯器(tsc, typescript compiler)的一些行為, 比如指定哪些檔案需要讓tsc來編譯, 哪些檔案不想讓tsc進行編譯之類的.

angular專案的tsconfig.json檔案

tsconfig.json
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "target": "es2015",
    "module": "es2020",
    "lib": [
      "es2018",
      "dom"
    ]
  },
  "angularCompilerOptions": {
    "enableI18nLegacyMessageIdFormat": false,
    "strictTemplates": true
  }
}

這其中angularCompilerOptions顧名思義是angular專用的, 不在本文討論範圍.

include, exclude, files設定項

include: 指定要編譯哪些檔案, 比如只需要編譯<project-dir>/src目錄下的.ts原始碼檔案

{
    "compilerOptions": {
        ...
    },
    include: ["./src/**/*", "./demo/**/*.tsx?"]
}

上面的include設定用到了兩個萬用字元: **/, *

**/表示匹配任何子路徑, 包括目錄分隔符/也會被它匹配, 所以用來這個萬用字元後, 目錄下有多少子目錄都會被匹配到

*表示匹配除了目錄分隔符(/)外的任何長度的字串

?表示匹配一個除檔案分隔符(/)外的任一字元

顯然./src/**/*即表示匹配src資料夾下的任何子資料夾的任何檔案; 而./demo/**/*.tsx?即表示匹配demo目錄下任何子目錄下的任意以.ts.tsx結尾的檔案

include其實就是一個白名單, 在這個白名單裡被匹配到的檔案才會被tsc處理編譯

相對於include是作為白名單的設定, exclude選項就是一個黑名單了, 它的值和include一樣是一個路徑名字串陣列, 最常見的用處就是用來排除掉node_modules目錄下的檔案

{
    "compilerOptions": {
        ...
    },
    include: ["./src/**/*", "./demo/**/*.tsx?"],
    exclude: ["node_modules/**/*"]
}

當然也可以用exclude排除掉include裡面包含到的檔案

有些情況即使exclude了某些檔案後, 編譯後的程式碼中可能仍然包含被exclude了的內容, 比如通過import匯入了被exclude了的node_modules資料夾, 此時tsc仍然會去處理被exclude了的檔案, 這一點應該不難理解

files 設定的作用類似include, 也是一個白名單路徑陣列, 不同在於它不能使用萬用字元, 而必須使用精確的檔案路徑(可以是相對路徑), 比如如果專案只有一個入口檔案, 那麼就可以使用在只用files設定這個檔案的路徑, 然後其他的檔案都通過index.tsimport

tsconfig.json
{
    "compilerOptions": {
        ...
    },
    // include: ["./src/**/*", "./demo/**/*.tsx?"],
    // exclude: ["node_modules/**/*"]
    files: ['./src/index.ts']
}

extends設定

extends 用於在一個tsconfig.json檔案中擴充套件其他tsconfig.json檔案, 比如angular專案中有三個tsconfig組態檔: tsconfig.json, tsconfig.spec.json, tsconfig.app.json

tsconfig.json
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "target": "es2015",
    "module": "es2020",
    "lib": [
      "es2018",
      "dom"
    ]
  },
  ...
}
tsconfig.app.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./out-tsc/app",
    "types": []
  },
  "files": [
    "src/main.ts",
    "src/polyfills.ts"
  ],
  "include": [
    "src/**/*.d.ts"
  ]
}
tsconfig.spec.json
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
  "extends": "./tsconfig.json",
  ...
  "files": [
    "src/test.ts",
    "src/polyfills.ts"
  ],
  "include": [
    "src/**/*.spec.ts",
    "src/**/*.d.ts"
  ]
}

從命名和檔案內容上即可看出之所以這麼做是為了針對測試檔案.spec.ts和普通.ts檔案在使用不同的設定時又能共用他們相同部分的設定, 達到避免重複的目的

compilerOptions下的設定

compilerOptions.allowUnreachableCode

表示是否允許程式碼中出現永遠無法被執行到的程式碼, 可選值是undefined, false, true

{
    "compilerOptions": {
        "allowUnreachableCode": false
        ...
    },
    ...
}

什麼是"永遠無法被執行到的程式碼"?

const foo = () => {
    return 0;

    console.log('aaa'); // 這一行程式碼就是永遠被執行到的程式碼
}

設定為undefined時, 遇到這種情況會給出warning, 設定false則直接編譯時丟擲錯誤無法成功編譯, 設定為true既沒有警告也沒有錯誤

compilerOptions.allowUnusedLabels

這個選項是針對標籤(label)語法的, 這個語法很罕見, 我也是看到了這個設定才知道有這個原來js還有Label語法, label語法有點像其他語言裡的goto, 真是場景中幾乎不用

compilerOptions.allowUnusedLabels表示是否允許未使用到的標籤

可選項:

  • undefined: 這是預設值, 碰到未使用的標籤給出warning警告
  • false: 碰到未使用的標籤丟擲錯誤, 編譯失敗
  • true: 碰到未使用的標籤編譯通過, 且不給出異常
function bar() {
    console.log('loafing...');

    loop: for (let i = 0; i < 3; i++) {
        for (let j = 0; j < 3; j++) {
            if (i === 2) {
                // break loop;
            }
            console.log(i, j, i + j);
        }
    }
}

compilerOptions.alwaysStrict

預設值是true, 開啟這個選項保證輸出的js程式碼處於ECMAScript標準的嚴格模式下, 也就是js檔案裡的use strict

compilerOptions.exactOptionalProperties

這是typescript4.4中才加入的一個選項, 預設處於不開啟狀態; 開啟此選項, typescript會對可空屬性執行更嚴格的型別檢查, 可空屬性只有在初始化時可以留空為undefined, 但是不能被手動設定為undefined

例如有一個IFoo介面

interface IFoo {
  foo?: string;
}

compilerOptions.exactOptionalProperties = false情況下

const obj: IFoo = {};
obj.foo = '1111';
console.log(obj.foo);
obj.foo = undefined;
console.log(obj.foo);

這段程式碼可以正常編譯通過

但如果開啟compilerOptions.exactOptionalProperties選項後情況就不同了

const obj: IFoo = {};
obj.foo = '1111';
console.log(obj.foo);

// 編譯器會報: Type 'undefined' is not assignable to type 'string' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
obj.foo = undefined;
console.log(obj.foo);

// 這一行會報: Type '{ foo: undefined; }' is not assignable to type 'IFoo' with 'exactOptionalPropertyTypes: true'. 
const obj2: IFoo = {
  foo: undefined
}

compilerOptions.downlevelIteration

先解釋下什麼是Downleveling? Downleveling是Typescript中的一個術語, 它表示將typescript程式碼轉譯為相對更低版本的javascript

這個標誌位元型樣是不開啟的.

開啟這個標誌位typescript會生成一個幫助方法來對es6中的for of和陣列展開([...arr])語法進行轉譯, 以相容es3/es5

下面的範例用for of迴圈並輸出一個包含符號表情的字串:

const text = `(