typeScript 配置檔案該怎麼寫?

lucifer發表於2020-08-25

TypeScript 的學習資料非常多,其中也不乏很多優秀的文章和教程。但是目前為止沒有一個我特別滿意的。原因有:

  • 它們大多數沒有一個清晰的主線,而是按照 API 組織章節的,內容在邏輯上比較零散。
  • 大多是“講是什麼,怎麼用“,而不是”講為什麼,講原理“。
  • 大多數內容比較枯燥,趣味性比較低。都是乾巴巴的文字,沒有圖片,缺乏能夠引起強烈共鳴的例子。

因此我的想法是做一套不同市面上大多數的 TypeScript 學習教程。以人類認知的角度思考問題,學習 TypeScript,通過通俗易懂的例子和圖片來幫助大家建立 TypeScript 世界觀。

系列安排:

目錄將來可能會有所調整。

注意,我的系列文章基本不會講 API,因此需要你有一定的 TypeScript 使用基礎,推薦兩個學習資料。

結合這兩個資料和我的系列教程,掌握 TypeScript 指日可待。

接下來,我們通過幾個方面來從巨集觀的角度來看一下 TypeScript。

<!-- more -->

前言

這篇文章是我的 TypeScript 系列的第 5 篇。今天我們就來看下, TypeScript 的配置檔案 tsconfig.json 該如何寫。

和 package.json 一樣, 它也是一個 JSON 檔案。package.json 是包描述檔案,對應的 Commonjs 規範,而 tsconfig.json 是最終被 TypeScript Compiler 解析和使用的一個 JSON 檔案。 TypeScript Compiler 用這個配置檔案來決定如何對專案進行編譯。

說到編譯,不得不提一個知名選手 - babel。 和 TypeScript 類似, 他們都可以將一種語法靜態編譯成另外一種語法。如果說我想編譯一個檔案,我只需要告訴 babel 我的檔案路徑即可。

npx babel script.js

有時候我想編譯整個資料夾:

npx babel src --out-dir lib

babel 也可以指定輸出目錄,指定需要忽略的檔案或目錄等等, TypeScript 也是一樣!你當然可以像 babel 一樣在命令列中全部指定好,也可以將這些配置放到 tsconfig.json 中,以配置檔案的形式傳遞給 TypeScript Compiler 。 這就是 tsconfig.json 檔案的初衷,即接受使用者輸入作為配置項。

初探 tsconfig

我們先來看一個簡單的 tsconfig 檔案。

{
  "compilerOptions": {
    "outDir": "./built",
    "allowJs": true,
    "target": "es5"
  },
  "include": ["./src/**/*"]
}

如上配置做了:

  • 讀取所有可識別的 src 目錄下的檔案(通過 include)。
  • 接受 JavaScript 做為輸入(通過 allowJs)。
  • 生成的所有檔案放在 built 目錄下(通過 outDir)。
  • 將 JavaScript 程式碼降級到低版本比如 ECMAScript 5(通過 target)。

實際專案有比這個更復雜。 接下來, 我們來進一步解讀。 不過在講配置項之前,我們先來看下 tsconfig.json 是如何被解析的。

tsconfig 是如何被解析的?

如果一個目錄下存在一個 tsconfig.json 檔案,那麼意味著這個目錄是 TypeScript 專案的根目錄。 如果你使用 tsc 編譯你的專案,並且沒有顯式地指定配置檔案的路徑,那麼 tsc 則會逐級向上搜尋父目錄尋找 tsconfig.json ,這個過程類似 node 的模組查詢機制。

如圖:

  • 在 _uglify-js@3.7.2@uglify-js 下執行 tsc 則會找到 配置檔案 1,在 _uglify-js@3.7.2@uglify-js/bin 下執行 tsc 也會找到 配置檔案 1
  • 同理在 lib,node_modules 也會找到 配置檔案 1
  • 在 _uglify-js@3.7.2@uglify-js/bin/lucifer 下執行 tsc 則會找到 配置檔案 2
  • 在 _uglify-js@3.7.2@uglify-js/lib/lucifer 下執行 tsc 則會找到 配置檔案 3

我在 上帝視角看 TypeScript 一種講述了 TypeScript 究竟做了什麼,帶你從巨集觀的角度看了一下 TypeScript。 其中提到了 TypeScript 編譯器會接受檔案或者檔案集合作為輸入,最終轉換為 JavaScript(noEmit 為 false) 和 .d.ts(declarations 為 true)。

這裡其實還少了一個點,那就是除了接受檔案或者檔案集合作為輸入,還會接受 tsconfig.json。tsconfig.json 的內容決定了編譯的範圍和行為,不同的 配置可能會得到不同的輸出,或者得到不同的檢查結果。

當 tsc 找到了一個 tsconfig.json 檔案,那麼其規定的編譯目錄則全部會被 typescript 處理,當然也包括其依賴的檔案。 如果 tsc 沒有找到一個 tsconfig.json 或 tsconfig 沒有有效資訊,那麼 tsc 會使用預設配置。 比如 tsconfig 是一個空的就沒有有效資訊:

{}
tsconfig 的全部屬性,以及屬性的預設值可以在這裡找到: http://json.schemastore.org/t...

總結一下 tsc 解析 tsconfig.json 的邏輯。

  • 如果命令列指定了配置選項或者指定了配置檔案的路徑,那麼直接會讀取。

    • 根據 tsconfig json schema 校驗是否格式正確。

      • 如果正確,則將其和預設配置合併(如果有 extends 欄位,也會一起合併),將合併後的配置傳遞給 TypeScript 編譯器並開始編譯。
      • 否則丟擲錯誤
  • 否則,會從當前目錄查詢 tsconfig.json 檔案, 如果找不到則逐層向上搜尋父目錄。

    • 如果找到了則會去根據 tsconfig json schema 校驗是否格式正確。

      • 如果正確,則將其和預設配置合併(如果有 extends 欄位,也會一起合併),將合併後的配置傳遞給 TypeScript 編譯器並開始編譯。
      • 否則丟擲錯誤
    • 否則,始終找不到則直接使用預設配置

tsconfig 的頂層屬性

tsconfig 的頂層屬性(Top Level)不多,主要有:compilerOptions, files, include, exclude,extends,compileOnSave等。

  • compilerOptions 是重頭戲,其屬性也是最多的,我們的專案也是對這個定製比較多,這個我後面會重點講。
  • files 則是你需要編譯的檔案
  • exclude 則是你不需要編譯的檔案目錄(支援 glob)
  • include 是你需要編譯的檔案目錄(支援 glob)
  • extends 就是繼承另外一個配置檔案,TypeScript 會對其進行合併,多專案公共配置有用。你也可以直接繼承社群的“最佳實踐”,比如:
{
  "extends": "@tsconfig/node12/tsconfig.json",

  "compilerOptions": {},

  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
  • compileOnSave 則是和編輯器(確切地說是檔案系統)聯動的配置,即是否在檔案儲存後進行編譯,實際專案不建議使用。

除了 compilerOptions,其他也相對比較好理解。 因此接下來我只針對 compilerOptions 詳細講解一番。

tsconfig 的編譯項

詳細全面的內容,大家只需要參考官網的就好了。官網寫的不僅全面,而且做了分類,非常清晰。

接下來,我會根據功能分開講幾個常用 的配置。

檔案相關

常用的是以下四個,由於前面已經做了介紹,因此就不贅述了。

  • exclude
  • extends
  • files
  • include

嚴格檢查

  • alwaysStrict

預設:false

首次釋出版本:2.1

這個是和 ECMAScript 規範相關的,工作機制和 ES 5 的嚴格模式一樣, 並且輸出的 JS 頂部也會也會帶上 'use strict'。

  • noImplicitAny(推薦開啟)

預設:true

首次釋出版本:-

我在 - TypeScript 型別系統 中提到了如果不對變數顯式宣告型別,那麼 TypeScript 會對變數進行型別推導,這當然也有推導不出的情況,這個時候該變數的型別就是 any,這個叫做隱式 any。區別於顯式 any:

const a: any = {};

隱式 any 是 TypeScript 編譯器推斷的。

  • noImplicitThis(推薦開啟)

預設:true

首次釋出版本:2.0

和隱式 any 型別, 只不過這次是針對的特殊的一個關鍵字 this,也就是你需要顯式地指定 this 的型別。

  • strict(推薦開啟)

預設:true

首次釋出版本:2.3

實際上 strict 只是一個簡寫,是多個規則的合集。 類似於 babel 中外掛(plugins)和 預設(presets)的差別。換句話說如果你指定了 strict 為 true ,那麼所有嚴格相關的規則的都會開啟,我所講的嚴格檢查都是,還有一部分我沒有提到的。另外將來如果增加更多嚴格規則,你只要開啟了 strict 則會自動加進來。

模組解析

模組相關

目的:allowSyntheticDefaultImports,allowUmdGlobalAccess,esModuleInterop,moduleResolution 都是為了和其他模組化規範相容做的。

  • allowSyntheticDefaultImports
  • allowUmdGlobalAccess
  • esModuleInterop
  • moduleResolution

還有一個配置 module,規定了專案的模組化方式,選項有 AMD,UMD,commonjs 等。

路徑相關

目的: baseUrl,paths,rootDirs, typeRoots,types 都是為了簡化路徑的拼寫做的。

  • baseUrl

這個配置是告訴 TypeScript 如何解析模組路徑的。比如:

import { helloWorld } from "hello/world";

console.log(helloWorld);

這個就會從 baseUrl 下找 hello 目錄下的 world 檔案。

  • paths

定義類似別名的存在,從而簡化路徑的書寫。

  • rootDirs

注意是 rootDirs ,而不是 rootDir,也就是說根目錄可以有多個。 當你指定了多個根目錄的時候, 不同根目錄的檔案可以像在一個目錄下一樣互相訪問。

實際上也有一個叫 rootDir 的, 和 rootDirs 的區別就是其只能指定一個。
  • typeRoots
  • types

types 和 typeRoots 我在 - types 和 @types 是什麼? 已經講得很清楚了,這裡就不多說了。

專案配置

JavaScript 相關

  • allowJs

預設:false

首次釋出版本:1.8

顧名思義,允許在 TypeScript 專案中使用 JavaScript,這在從 JavaScript 遷移到 TypeScript 中是非常重要的。

  • checkJs

預設:false

首次釋出版本:-

和 allowJs 類似, 只不過 checkJs 會額外對 JS 檔案進行校驗。

宣告檔案相關

如果 TypeScript 是將 TS 檔案編譯為 JS,那麼宣告檔案 + JS 檔案就可以反推出 TS 檔案。

這兩個用來生成 .d.ts 和 .d.ts 的 sourcemap 檔案。

  • declaration

預設:false

首次釋出版本:1.0

  • declarationMap

預設:false

首次釋出版本:2.9

外部庫相關

  • jsx

預設:react

首次釋出版本:2.2

這個是告訴 TypeScript 如何編譯 jsx 語法的。

  • lib

預設:-

首次釋出版本:2.0

lib 我在 TypeScript 型別系統 中講過。 Typescript 提供了諸如 lib.d.ts 等型別庫檔案。隨著 ES 的不斷更新, JavaScript 型別和全域性變數會逐漸變多。Typescript 也是採用這種 lib 的方式來解決的。

(TypeScript 提供的部分 lib)

輸出相關

outDir 和 outFile 這兩個配置則是告訴 TypeScript 將檔案生成到哪裡。

  • outDir

預設:和 ts 檔案同目錄(且同名,只是字尾不同)

首次釋出版本:-

  • outFile

預設:-

首次釋出版本:1.0

module 是 CommonJS 和 ES6 module 不能知道 outFile,只有是 None, System 或 AMD 才行,其會將這些模組的檔案內容打包到全域性檔案內容之後。

而 noEmit 則是控制是否輸出 JS 檔案的。

  • noEmit

預設:false

首次釋出版本:-

如果你只希望用 TypeScript 進行型別檢查,不希望要它生成檔案,則可以將 noEmit 設定成 true。

  • target

即輸出的 JavaScript 對標的 ECMA 規範。 比如 “target”: “es6” 就是將 es6 + 的語法轉換為 ES6 的 程式碼。其選項有 ES3,ES5,ES6 等。

為什麼沒有 ES4 ? ^_^

總結

  • tsconfig 就是一個 JSON 檔案,TypeScript 會使用該檔案來決定如何編譯和檢查 TypeScript 專案。和 babel 類似,甚至很多配置項都是相通的。
  • 如果一個目錄下存在一個 tsconfig.json 檔案,那麼意味著這個目錄是 TypeScript 專案的根目錄。 如果你使用 tsc 編譯你的專案,並且沒有顯式地指定配置檔案的路徑,那麼 tsc 則會逐級向上搜尋父目錄尋找 tsconfig.json ,這個過程類似 node 的模組查詢機制。
  • tsconfig 中最重要的恐怕就是編譯器選項(compilerOptions)了。如果你按照功能去記憶則會比較簡單, 比如檔案相關的有哪些, 嚴格檢查的有哪些,宣告檔案的有哪些等等。

參考

關注我

大家也可以關注我的公眾號《腦洞前端》獲取更多更新鮮的前端硬核文章,帶你認識你不知道的前端。

知乎專欄【 Lucifer - 知乎

點關注,不迷路!

相關文章