[TS] TypeScript Config File (tsconfig)
此篇為各筆記之整理,非原創內容,資料來源可見下方連結與文後參考資料。
TSC
$ npx tsc -b . # build
$ npx tsc -b . --clean # 移除掉打包後產生的檔案
$ npx tsc --showConfig > ts-config.txt # 檢視實際上被 TypeScript 處理的檔案(最終吃到的設定檔)
$ npx tsc --project tsconfig.json --diagnostics > ts-diagnostics.txt
如果專案中有多個 tsconfig
檔案,用 references
把對應的 configuration 組在一起,例如(同時有 tsconfig.node.json
和 tsconfig.app.json
,template-react-ts/tsconfig.json @ GitHub),則使用 tsc -b
來執行才會對每一個 tsconfig 的檔案來做檢查,如果只是執行 tsc
或 tsc --project tsconfig.json
,則可能不會檢查到所有檔案。
參考:
Configuration
- TSConfig Reference @ TypeScript
- The TSConfig Cheat Sheet | Total TypeScript @ Matt Pocock
- React with TypeScript: Best Practices @ SitePoint
- Troubleshooting Handbook: tsconfig.json @ react-typescript-cheatSheet
設定檔
建議可以參考 The TSConfig Cheat Sheet @ Total TypeScript 的設定
如果是用 VS Code 的話,滑鼠移上去 property 就會顯示每個設定的描述:
- compilerOptions
baseUrl
:設成"baseUrl": "./src"
則可以使用 absolute path 來載入模組
include
: 如果使用 glob pattern 但沒有指定副檔名的話,預設只會使用有支援的副檔名(例如,.ts
、.tsx
和.d.ts
,如果allowJs
是true
的話,則會一併啟用.js
和.jsx
),這些指定的檔案會從tsconfig.json
所放置的位置作為相對目錄。exclude
:從已經被include
中的檔案中加以排除掉。extends
- 被繼承檔案的設定會先被載入,接著被該檔案內的設定加已覆蓋
- 所有在設定檔中的相對路徑,都會根據原設定檔的位置加以解析
file
,include
和exclude
的內容會覆蓋掉被繼承的檔案中所寫的- 唯一一個不會被繼承的屬性是
references
- 帶有相對路徑的屬性不會被繼承,而是會根據原設定檔中的相對路徑被解析
Type Checking 相關
建議打開下述的 flag:
- noImplicitAny
- noUncheckedIndexedAccess:可以避免取用到不存在的 object property。
- useUnknownInCatchVariables:讓 catch 中的 error 預設是
unknown
type。 - exactOptionalPropertyTypes:讓 TypeScript 可以明確區分在定義型別時 optional (
?
) 或undefined
的差異
Project 相關
incremental
:把上一次 compilation 的資訊紀錄並保存下來,有助於加快下次執行 type-check 或 emit 所需的時間(參考:Faster subsequent builds with the--incremental
flag)。
Module 相關
module
:指程式打包後使用的 module system,例如commonjs
、umd
、node16
、es2022
、...等。
module
主要指的是打包後使用的 module system,主要指和 module 有關的語法;另一個欄位 target
指的則是「程式語法」本身打包後要支援到的環境版本,例如,如果環境不支援 arrow function,則打包後會編譯成一般的 function 語法。
Debug 相關
除了在執行 tsc 時加上 --showConfig
外(即,npx tsx --showConfig
),也可以在 tsconfig.json
中使用下述設定來 debug
diagnostics
:顯示診斷報告,可以用來看 performance,例如,執行的時間、檢查了多少檔案、消耗的記憶體explainFiles
:顯示 TypeScript 處理了那些檔案,以及之所以被 TypeScript 檢查的原因extendedDiagnostics
:相較於diagnostics
更完整的報告generateCpuProfile
listEmittedFiles
:顯示有那些被 emit 出來的檔案listFiles
:顯示有哪些檔案會被 TypeScript 給 compile,可以用來確定檔案有沒有被 TS 處理到,如果希望進一步了解回什麼會被 TS 處理到的原因,則可以改用explainFiles
traceResolution
// tsconfig.json
/* Visit https://aka.ms/tsconfig.json to read more about this file */
{
"compilerOptions": {
/* Basic Options */
"target": "ES2018", // compile 後要支援到的 ECMAScript 版本
"module": "commonjs", // compile 後程式碼會用 commonjs 來處理模組的匯出匯入
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": false, // 是否允許匯入 js 檔,會影響到 include 時解析的副檔名是否包含(.js 和 .jsx)
"declaration": true,
// 如果沒設定會套用預設值(TS 自動判斷)
// 有「機會」導致 src 的資料夾也一起被 build 到 dist 資料夾中
// 例如 TS 自動把 rootDir 判斷為 { "rootDir": "." }
"rootDir": "src",
// 預設 tsc 會直接把編譯好的 js 檔放在與 ts 檔相同的路徑, 但這樣檔案會很散亂,因此全部放到 dist
"outDir": "dist",
/* Strict Type-Checking Options */
"strict": true,
/* Additional Checks */
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true, // 確保 switch case 都會有 break 或 return
/* Module Resolution Options */
"baseUrl": "./", // 匯入模組時,路徑可以使用從 baseUrl 開始,而不需要 ./../ 這種寫法
"paths": {
"@": ["src"] // 將 @ map 到 src
},
"esModuleInterop": false,
/* Source Map Options */
"sourceMap": true,
/* Advanced Options */
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true // 確保檔案的大小寫一致,避免某些作業系統對大小寫不敏感
},
"include": ["src/**/*.ts"], // compiler 要做用在哪些檔案上
"exclude": []
}
如何提升 TypeScript compile、type-check、和 emit 的效率
keywords: performance
檢視編譯效能
首先,要檢視效能可以使用 tsconfig.json
中的 diagnostics
或 extendedDiagnostics
(完整的資訊)的設定,如此會顯示 TypeScript compile 時所消耗的時間。
了解 TypeScript 處理了那些檔案
接著先看 TypeScript 實際處理了那些檔案:
- 可以使用
--showConfig
來檢視設定
npx tsc --showConfig > tsconfig.txt
- 或者在
tsconfig.json
中使用listFiles
或explainFiles
的 option 來檢視實際被 TypeScript 處理到的檔案
這時候也許可以看到一些不需要但被 TypeScript 處理的檔案,例如 storybook_release
、public
等等:
使用 exclude 排除不需要的檔案
把這些檔案加到 tsconfig.json
中的 exclude
中:
// tsconfig.json
{
"exclude": ["node_modules", "public", "storybook_release", "coverage"]
}
使用 incremental flag
- Faster subsequent builds with the --incremental flag @ TypeScript Docs
- Performance @ TypeScript Github
可以在 tsconfig.json
中使用 incremental
的 option(如果專案本身時屬於 composite,則預設就會是 true
)。
incremental 的功能讓 TypeScript 可以將前一次 compile 的資 訊紀錄在名為 .tsbuildinfo
的檔案中,這麼做可以讓 TypeScript 知道只有那些最少的檔案需要被 re-checked/re-emitted,大幅減少 type-check 所需的時間。另外,這個檔案刪掉也沒關係,因為它單純只是用來加快 compile 速度的。
Restart TS Server
做完這些設定後,如果 VSCode 有一點「怪怪的」,可以重新啟動 VSCode 的 TypeScript Server:
FAQ
src 資料夾也被 build 進 dist 資料夾中
在 tsconfig.json
中,加上 "rootDir": "src"
(原因見上方說明)。
JSX 的選項要用那個?
- Which value should I use for
jsx
? @ TotalTypeScript - Explained: 'React' refers to a UMD global @ TotalTypeScript
- JSX @ TypeScript > tsconfig
{
"compilerOptions": {
// "preserve":如果你不是用 tsc 來 compile App
// "react-jsx":如果你是用 tsc 來 compile App,且 React 的版本是 17 以上
// "jsx":如果你是用 tsc 來 compile App,且 React 的版本是 16 以下
"jsx": "preserve" // "preserve", "react"
}
}
在撰寫 React 時,有可能會看到這個錯誤訊息:
React refers to a UMD global, but the current file is a module. Consider adding an import instead.
這個錯誤訊息很可能是和在 tsconfig 中的 jsx
設定有關,這個項目是用來定義 .tsx
的檔案要如何在 build 時轉換成 .js
。然而在多數的 React 專案中,都不會使用 tsc 來將專案做打包,TypeScript 單純是用作型別檢查,而不用來打包專案。
之所以會有這個錯誤,是因為預設的值是 jsx: react
,這時候因為在打包後的檔案中會使用 React.createElement
,因此就會需要在每次使用到 JSX 時,import React,否則會出現錯誤(參考這裡)。
既然我們並不是使用 TypeScript 來做打包,我們就可以請 TypeScript 不要管如何處理 .tsx
的打包方式:
jsx: preserve
:就表示如果使用 TS 打包時,保持 JSX 不變jsx: react-jsx
:如果你使用的是 React 17 以後的版本,因為在 React 17 之後,並不需要在每次使用 JSX 時都 import React,這時候可以使用react-jsx
這個選項
簡單來說,在目前多數的 React 專案中,可以使用 preserve
或 react-jsx
的選項,而不是預設的 react
。