一些你需要掌握的 Tsconfig.Json 常用配置項
大家好,我是前端西瓜哥。
tsconfig.json 是用來配置 TS 編譯選項的,通常位于項目的根目錄位置。
我們可以用 ts 提供的 tsc 命令行工具,執(zhí)行 tsc --init。
復制$ tsc --init
Created a new tsconfig.json with: TS
target: es2016
module: commonjs
strict: true
esModuleInterop: true
skipLibCheck: true
forceConsistentCasingInFileNames: true
You can learn more at https://aka.ms/tsconfig
然后我們就能得到一個默認的 tsconfig.json 文件,且這是一種可以添加注釋的 json 文件。
里面有很多帶有注釋的選項,目的是讓開發(fā)者能夠反注釋快速啟用一些配置。
但注釋的選項太多了,所以我將它們移除了,得到下面的默認配置:
復制{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
頂層配置
首先我們看配置最上層級的配置字段。
- compilerOptions:編譯器相關的選項。比如配置編譯成 ES5,模塊化使用 commonjs 等。這里的編譯配置很多,后面我們會講解一些常用的配置;
- files:指定需要被編譯的文件列表。這里不能指定目錄,只能是文件,可以省略.ts 后綴。適合需要編譯的文件比較少的情況。默認值為 false;
- include:指定需要編譯的文件列表或匹配模式。include 可以通過通配符指定目錄,如"src/**/*" 表示 src 下的所有文件。如果沒有指定 files 配置,默認值為 ** ,即項目下所有文件;如果配置了 files,默認值為 [] 空數(shù)組;
- exclude:在 include 圈定的范圍內,排除掉一些文件。我們經常用它來排除編譯輸出目錄、測試文件目錄、一些生成文件的腳本等文件。默認值為 "node_modules,bower_componen";
- extends:繼承另一個 ts 配置文件。這在 monorepo 的代碼組織中非常有用,不同的 package 可以通過 extends 繼承通用的 ts 配置。用法示例:"extends": "./common-tsconfig.json"。
- reference:引用。項目中如果有多個相互獨立的模塊,可以使用這個屬性來做分離。這樣一個模塊改變后,就只重新編譯這個模塊,其他模塊不重新編譯。編譯時要改用tsc --build。這在非常大的項目中應該能有不小收益。
需要注意的是,files、include、exclude 只是指定編譯的入口文件范圍,如果其中的文件 import 了范圍外的 ts 文件,范圍外的文件依舊會被編譯。
在 VSCode 下,范圍外的 ts 文件不會應用項目下的 tsconfig.json 配置。
常用的編譯器配置(compilerOptions)
接下來我們就來看看 compilerOptions 下的常用配置屬性。
因為配置項實在很多,我就挑一些比較基本的進行講解。
target
指定編譯的目標版本。
tsc 也可以像 babel 一樣,可以將高版本的 TS / JS 編譯為低版本。你看這個 tsc 腳本多大。
target 用于指定 TS 最后編譯出來的 ES 版本,默認值是 ES3。
對于一些高版本引入的新 API 并,tsc 不會注入 polyfill,你需要自己全量引入 core-js,這點還是 babel 提供的按需引入 core-js 要更好一些。
當然其他的不能 polyfill 的實現(xiàn),tsc 還是會做處理的。比如箭頭函數(shù)轉換為普通函數(shù),async / await 轉換為一大坨的等價代碼。
說實在的,ES3 實在有夠古老的,很多 API 都不支持,個人覺得默認為 ES5 比較好。
我想大概是歷史原因,因為 TS 發(fā)布那會,ES6 還沒出來,只有 ES5 編譯成 ES3 這一種情況?,F(xiàn)在雖然 ES5 已經廣泛支持了,但為了兼容還是保持默認的 ES3。
target 支持的值有:es3、es5、es6(也叫 es2015)、es2016 一直到 es2022、然后還有 esnext。沒有 es7 這種東西,你得用 es2016。另外,esnext 指的是當前版本的 TS 編譯器支持的最高版本。
這些值是大小寫敏感的,可以是 es5、ES5,或大小寫混雜。
通常來說前端項目會使用 es5。后端項目就看 nodejs 的版本支持 ES 的程度,像 Nestjs 腳手架生成的項目,taget 指定為 es2017。
lib
TypeScript 默認自帶通用的 JS 內置 API 的類型聲明,比如 Math、RegExp 等。
但 JS 運行的環(huán)境各種各樣,會有一些特有的全局對象,比如瀏覽器下的 document,新的 ES 版本引入的新的 API。
為此,我們可以用 lib 這個屬性來設置需要引入的全局類型聲明。
lib 有高層級的:ES5、ES2015、DOM 、ESNEXT、WebWorker、ScriptHost 等?;蚴堑蛯蛹壞K的 DOM.Iterable、ES2015.Core、ES2017.TypedArrays、 ES2018.Intl 等。高層級通常是多個全局類型聲明的組合。
lib 的默認值通過 target 來指定,比如你的 target 指定為 ES7,它就會引入 ES7 的全局類型(大概是 lib.es2016.full.d.ts)。
但如果你想用最新版本的 ES 語法,但希望它能編譯成兼容性良好的 ES5,你就要手動設置 lib,像下面這樣:
復制"target": "ES5",
"lib": [
"DOM",
"DOM.Iterable",
"ESNext"
]
lib 可以引入的全局類型聲明文件都在這個目錄下:
https://github.com/microsoft/TypeScript/blob/main/lib。
strict
啟用嚴格模式,能夠更能保證類型檢測的正確。
將 strict 設置為 true,會開啟一系列的嚴格的類型檢驗配置。
比如 strictNullChecks 配置的默認值會變成 true。這樣一些對象類型就不能賦值為 undefined 或 null,就能一定程度阻止 obj.prop 可能導致的 Cannot read properties of undefined 的運行時錯誤。
還比如 strictBindCallApply 默認值變成 true。此時,對函數(shù)使用 bind、call、apply,參數(shù)類型必須和原函數(shù)類型相同。如果是 false,則可以是任何類型。
此外還有很多其他的和嚴格模式相關的配置也會開啟。
建議開啟 strict,能減少 bug,缺點是要多寫一些類型推斷和分支判斷的代碼。
baseUrl
baseUrl 用于設置基礎 url,可以幫我們省掉一些多余的路徑前綴。
比如我們原來要寫長長的:
復制import { Login } from "./src/features/user/login";
但如果我們設置 baseUrl 為 ./src,我們使用絕對路徑時就能去掉重復的前綴,將路徑寫短一些:
復制import { Login } from "features/user/login";
相對路徑不需要 baseUrl,因為它是相對于當前文件路徑計算的。
./src 的 . 為 tsconfig.json 配置文件所在的目錄路徑。其實寫成 src 也可以,它和 ./src 是等價的。
如果你不設置 baseUrl,模塊文件 import 需要使用相對路徑,或絕對路徑(不是針對項目根目錄的絕對路徑,而是完整的路徑)。
如果你想使用相對項目根目錄的路徑,你需要將 baseUrl 設置為 . 。
paths
路徑重映射。
要使用 paths,首先要設置好 baseUrl,paths 的源路徑和新路徑會使用 baseUrl 作為相對路徑計算。
復制"baseUrl": "./src",
"paths": {
"@lib/*": ["./other/_lib/*", "./other/_lib2/*"]
},
上面的配置,是將 other/_lib 和 other/_lib2 路徑重映射為 @lib。
這里的 @ 并不是必須的,這樣寫只是表明這個路徑是一個重映射,或者叫別名,實際上文件系統(tǒng)上不存在對應的真實目錄。
這樣,原來比較冗長的路徑:
復制import LibA from "other/_lib/lib_a";
就可以改為:
復制import LibA from "@lib/lib_a";
declaration
是否給每個編譯出來的 JS 生成對應的 d.ts 類型聲明文件。
TS 編譯后變成的 JS 是不攜帶類型信息的。如果你想要保留信息,就需要一個 d.ts 文件來描述對應的 JS 文件。
我們用 NPM 安裝的第三方包,這些包下的 package.json 文件的 types 屬性,就指定了這個包的類型文件。如果沒有顯式提供 types 屬性,則使用默認的 index.d.ts。
declarationDir
指定編譯生成的類型聲明文件輸出的目錄。不提供的話,默認和生成的 js 文件放在一起。
復制"declarationDir": "./types"
outDir
編譯文件的輸出目錄,默認為 .,即項目根目錄。如果不設置它,編譯后的文件就會和源文件混雜在一起。通常我們會將 outDir 設置為 "./dist"。
outFile
將所有 ts 文件合并編譯生成一個 js 文件和它的類型聲明 d.ts 文件。
這個配置項很少用,因為它只能用在不支持模塊化導入的系統(tǒng),即所有的 ts 文件都是全局的。
換句話說,module 配置項需要為 None、System 或 AMD。
復制"outFile": "./app.js"
module
編譯后的 JS 使用哪種模塊系統(tǒng)。
模塊系統(tǒng)常用的有兩種:ESModule 和 CommonJS。前者是 ES 的標準(使用了 import 關鍵字),后者則是 Nodejs 的使用的模塊系統(tǒng)(使用了 require)。此外還有 AMD、UMD 等。
支持的值有:none、commonjs、amd、umd、system、es6/es2015、es2020、es2022、esnext、node16、nodenext。
它們的具體不同可以看官方文檔的代碼示例:
https://www.typescriptlang.org/tsconfig#module。
如果 target 是 ES3 或 ES5,默認值是 CommonJS(畢竟 ES6 后才有的 ESModule);否則為 ES6/ES2015。
allowJs
將 js 文件也作為編譯對象,可以被 ts 文件引入。布爾值,默認為 false。
types
類型聲明的一種引入方式是 @types 包,比如 React 框架使用了 flow 作為類型系統(tǒng),為了支持 TypeScript,React 團隊又寫一套 d.ts 類型文件,發(fā)布到 @types/react 包上。
然后我們下載這個類型包后,并使用類似 import React from 'react',TS 會從從 node_modules/@types 中找到 react 文件夾,如果找不到,就會向上一層目錄繼續(xù)找,知道找到位置。如果存在,這個 React 對象就會被賦予聲明的類型。
@types 可以是模塊類型聲明(像 React 類型),也可以是全局類型聲明(如 nodejs 的 process 對象類型)。
types 配置 可指定只使用哪些全局類型聲明,而不是 node_modules/@types 下所有的類型聲明。如:
復制"lib": [
"node", // 即 node_modules/@types/node
"jest"
]
typeRoots
前面說到 ts 會遞歸查找 node_modules/@types 去尋找類型聲明文件。
但你也可以用 typeRoots 來 指定只尋找特定目錄下的類型聲明文件,如:
復制"typeRoots": ["./typings", "./vendor/types"]
結尾
tsconfig 的配置非常多,但我想基本上掌握上面這幾個配置的使用就差不多了。
更多的配置項可以看官方文檔,建議自己構建一個 TS 項目進行測試。