程式碼檢查
目前 TypeScript 的程式碼檢查主要有兩個方案:使用 TSLint 或使用 ESLint + typescript-eslint-parser
。
什麼是程式碼檢查
程式碼檢查主要是用來發現程式碼錯誤、統一程式碼風格。
在 JavaScript 專案中,我們一般使用 ESLint 來進行程式碼檢查。它透過外掛化的特性極大的豐富了適用範圍,搭配 typescript-eslint-parser
之後,甚至可以用來檢查 TypeScript 程式碼。
TSLint 與 ESLint 類似,不過除了能檢查常規的 js 程式碼風格之外,TSLint 還能夠透過 TypeScript 的語法解析,利用型別系統做一些 ESLint 做不到的檢查。
為什麼需要程式碼檢查
有人會覺得,JavaScript 非常靈活,所以需要程式碼檢查。而 TypeScript 已經能夠在編譯階段檢查出很多問題了,為什麼還需要程式碼檢查呢?
因為 TypeScript 關注的重心是型別的匹配,而不是程式碼風格。當團隊的人員越來越多時,同樣的邏輯不同的人寫出來可能會有很大的區別:
縮排應該是四個空格還是兩個空格?
是否應該禁用
var
?介面名是否應該以
I
開頭?是否應該強制使用
===
而不是==
?
這些問題 TypeScript 不會關注,但是卻影響到多人協作開發時的效率、程式碼的可理解性以及可維護性。
下面來看一個具體的例子:
存在的問題
tsc
是否報錯
eslint
是否報錯
tslint
是否報錯
myName
被誤寫成了 myNane
✅
✅
❌
toString
被誤寫成了 toStrng
✅️
❌
❌
少了一個分號
❌
✅
✅
上例中,由於 eslint
和 tslint
均無法識別 myName
存在哪些方法,所以對於拼寫錯誤的 toString
沒有檢查出來。
而程式碼風格的錯誤不影響編譯,故少了一個分號的錯誤 tsc
沒有檢查出來。
對於未定義的變數 myNane
,tsc
可以檢測出來。由於用到 tslint
的地方肯定會接入 tsc
編譯,所以 tslint
就沒必要檢測這個錯誤了。eslint
需要能夠獨立於某個編譯環境執行,所以能檢測出此類別錯誤,而對於 TypeScript 程式碼,這其實是一種冗餘的檢測了。
事實上,不止 tsc
與 eslint
之間有冗餘的檢測,tsc
與 tslint
之間也有一些冗餘的檢測,但是大部分都是因為早期的 tsc
還沒能做到檢測此類別錯誤。
舉個例子,TSLint 中的 typeof-compare
要求 typeof
表示式比較的物件必須是 'undefined'
, 'object'
, 'boolean'
, 'number'
, 'string'
, 'function'
或 'symbol'
之一。而 TypeScript 2.2 之後,編譯器就已經自帶了這個功能。
下圖表示了 tsc
, eslint
和 tslint
能覆蓋的檢查:
上圖中,tsc
, eslint
和 tslint
之間互相都有重疊的部分,也有各自獨立的部分。
雖然發現程式碼錯誤比統一的程式碼風格更重要,但是當一個專案越來越龐大,開發人員也越來越多的時候,程式碼風格的約束還是必不可少的。
應該使用哪種程式碼檢查工具
TSLint 與 ESLint 作為檢查 TypeScript 程式碼的工具,各自有各自的優點:
TSLint 的優點:
專為 TypeScript 服務,bug 比 ESLint 少
不受限於 ESLint 使用的語法樹 ESTree
能直接透過
tsconfig.json
中的配置編譯整個專案,使得在一個檔案中的型別定義能夠聯動到其他檔案中的程式碼檢查
ESLint 的優點:
基礎規則比 TSLint 多很多(249 : 151)
社群繁榮,外掛眾多(50+ : 9)
下面來看一些具體的例子:
以上程式碼在 TSLint 中會報錯,原因是加號兩邊必須同為數字或同為字串(需要開啟 restrict-plus-operands
規則)。
ESLint 無法知道加號兩邊的型別,所以對這種規則無能為力。
ESLint 可以檢測出來以上程式碼的函式引數超過了 7 個(需要開啟 max-params
規則)。
但是 TSLint 沒有此項檢查,雖然也可以實現,但是需要自己手動寫一條規則。
那麼到底該使用哪種程式碼檢測工具呢?經過一些實踐,我建議可以按照以下流程決定:
在 TypeScript 中使用 ESLint
安裝 ESLint
ESLint 可以安裝在當前專案中或全域性環境下,因為程式碼檢查是專案的重要組成部分,所以我們一般會將它安裝在當前專案中。可以執行下面的指令碼來安裝:
由於 ESLint 預設使用 Espree 進行語法解析,無法識別 TypeScript 的一些語法,故我們需要安裝 typescript-eslint-parser
,替代掉預設的解析器,別忘了同時安裝 typescript
:
由於 typescript-eslint-parser
對一部分 ESLint 規則支援性不好,故我們需要安裝 eslint-plugin-typescript
,彌補一些支援性不好的規則。
建立配置檔案
ESLint 需要一個配置檔案來決定對哪些規則進行檢查,配置檔案的名稱一般是 .eslintrc.js
或 .eslintrc.json
。
當執行 ESLint 的時候檢查一個檔案的時候,它會首先嚐試讀取該檔案的目錄下的配置檔案,然後再一級一級往上查詢,將所找到的配置合併起來,作為當前被檢查檔案的配置。
我們在專案的根目錄下建立一個 .eslintrc.js
,內容如下:
以上配置中,我們指定了兩個規則,其中 eqeqeq
是 ESLint 原生的規則(它要求必須使用 ===
或 !==
,禁止使用 ==
或 !=
,與 null
比較時除外),typescript/class-name-casing
是 eslint-plugin-typescript
為 ESLint 增加的規則(它要求類別和介面的命名必須遵守帕斯卡命名法,比如 PersianCat
)。
規則的取值一般是一個數組(上例中的 eqeqeq
),其中第一項是 off
、warn
或 error
中的一個,表示關閉、警告和報錯。後面的項都是該規則的其他配置。
如果沒有其他配置的話,則可以將規則的取值簡寫為陣列中的第一項(上例中的 typescript/class-name-casing
)。
關閉、警告和報錯的含義如下:
關閉:禁用此規則
警告:程式碼檢查時輸出錯誤資訊,但是不會影響到 exit code
報錯:發現錯誤時,不僅會輸出錯誤資訊,而且 exit code 將被設為 1(一般 exit code 不為 0 則表示執行出現錯誤)
檢查一個 ts 檔案
建立了配置檔案之後,我們來建立一個 ts 檔案看看是否能用 ESLint 去檢查它了。
建立一個新檔案 index.ts
,將以下內容複製進去:
然後執行以下命令:
則會得到如下報錯資訊:
上面的結果顯示,剛剛配置的兩個規則都生效了:介面 person
必須寫成帕斯卡命名規範,==
必須寫成 ===
。
需要注意的是,我們使用的是 ./node_modules/.bin/eslint
,而不是全域性的 eslint
指令碼,這是因為程式碼檢查是專案的重要組成部分,所以我們一般會將它安裝在當前專案中。
可是每次執行這麼長一段指令碼頗有不便,我們可以透過在 package.json
中新增一個 script
來建立一個 npm script 來簡化這個步驟:
這時只需執行 npm run eslint
即可。
檢查整個專案的 ts 檔案
我們的專案原始檔一般是放在 src
目錄下,所以需要將 package.json
中的 eslint
指令碼改為對一個目錄進行檢查。由於 eslint
預設不會檢查 .ts
字尾的檔案,所以需要加上引數 --ext .ts
:
此時執行 npm run eslint
即會檢查 src
目錄下的所有 .ts
字尾的檔案。
在 VSCode 中整合 ESLint 檢查
在編輯器中整合 ESLint 檢查,可以在開發過程中就發現錯誤,極大的增加了開發效率。
要在 VSCode 中整合 ESLint 檢查,我們需要先安裝 ESLint 外掛,點選「擴充套件」按鈕,搜尋 ESLint,然後安裝即可。
VSCode 中的 ESLint 外掛預設是不會檢查 .ts
字尾的,需要在「檔案 => 首選項 => 設定」中,新增以下配置:
這時再開啟一個 .ts
檔案,將滑鼠移到紅色提示處,即可看到這樣的報錯資訊了:
使用 AlloyTeam 的 ESLint 配置
ESLint 原生的規則和 eslint-plugin-typescript
的規則太多了,而且原生的規則有一些在 TypeScript 中支援的不好,需要禁用掉。
這裡我推薦使用 AlloyTeam ESLint 規則中的 TypeScript 版本,它已經為我們提供了一套完善的配置規則。
安裝:
在你的專案根目錄下建立 .eslintrc.js
,並將以下內容複製到檔案中:
使用 ESLint 檢查 tsx 檔案
如果需要同時支援對 tsx 檔案的檢查,則需要對以上步驟做一些調整:
安裝 eslint-plugin-react
eslint-plugin-react
package.json 中的 scripts.eslint 新增 .tsx
字尾
.tsx
字尾VSCode 的配置中新增 typescriptreact 檢查
使用 AlloyTeam ESLint 規則中的 TypeScript React 版本
AlloyTeam ESLint 規則中的 TypeScript React 版本
在 TypeScript 中使用 TSLint
TSLint 的使用比較簡單,參考官網的步驟安裝到本地即可:
建立配置檔案 tslint.json
為 package.json
新增 tslint
指令碼
其中 --project .
會要求 tslint
使用當前目錄的 tsconfig.json
配置來獲取型別資訊,很多規則需要型別資訊才能生效。
此時執行 npm run tslint
即可檢查整個專案。
在 VSCode 中整合 TSLint 檢查
在 VSCode 中安裝 tslint
外掛即可,安裝好之後,預設是開啟的狀態。
使用 AlloyTeam 的 TSLint 配置
AlloyTeam 為 TSLint 也打造了一套配置 tslint-config-alloy
安裝之後修改 tslint.json
即可
使用 TSLint 檢查 tsx 檔案
TSLint 預設支援對 tsx 檔案的檢查,不需要做額外配置。
Troubleshootings
Cannot find module 'typescript-eslint-parser'
你執行的是全域性的 eslint,需要改為執行 ./node_modules/.bin/eslint
。
cannot read property type of null
需要關閉 eslint-plugin-react
中的規則 react/jsx-indent
。
如果仍然報錯,多半是因為某些規則需要被關閉,可以使用「二分排錯法」檢查是哪個規則造成了錯誤。也歡迎給 eslint-config-alloy 提 issue。
VSCode 沒有顯示出 ESLint 的報錯
檢查「檔案 => 首選項 => 設定」中有沒有配置正確
檢查必要的 npm 包有沒有安裝
檢查
.eslintrc.js
有沒有配置檢查檔案是不是在
.eslintignore
中
如果以上步驟都不奏效,則可以在「檔案 => 首選項 => 設定」中配置 "eslint.trace.server": "messages"
,按 Ctrl
+Shift
+U
開啟輸出面板,然後選擇 ESLint 輸出,檢視具體錯誤。
為什麼 ESLint 無法檢查出使用了未定義的變數(no-undef
規則為什麼被關閉了)?
no-undef
規則為什麼被關閉了)?因為 typescript-eslint-parser
無法支援 no-undef
規則。它針對正確的介面定義會報錯。
所以我們一般會關閉 no-undef
規則。
為什麼有些定義了的變數(比如使用 enum
定義的變數)未使用,ESLint 卻沒有報錯?
enum
定義的變數)未使用,ESLint 卻沒有報錯?因為無法支援這種變數定義的檢查。建議在 tsconfig.json
中新增以下配置,使 tsc
編譯過程能夠檢查出定義了未使用的變數:
啟用了 noUnusedParameters 之後,只使用了第二個引數,但是又必須傳入第一個引數,這就會報錯了
第一個引數以下劃線開頭即可,參考 https://github.com/Microsoft/TypeScript/issues/9458
為什麼有的錯誤 TSLint 可以檢查出來,vscode 裡的 TSLint 卻檢查不出來?
因為 TSLint 依賴 tsconfig.json
獲得了型別資訊,而 vscode 裡的 TSLint 暫不支援獲取型別資訊,所以 no-unused-variable
就失效了。
不僅 no-unused-variables
失效了,TSLint rules 裡面所有標有 Requires Type Info
的規則都失效了。
Last updated