
在 ESLint v8.14.0 中,我貢獻了一個新的核心規則,名為 no-constant-binary-expression,它能夠偵測到各種微妙且有趣的錯誤,令我感到驚訝。
在這篇文章中,我將解釋此規則的作用,並分享一些真實錯誤的範例,這些錯誤是在 Material UI、Webpack、VS Code 和 Firefox 等熱門開放原始碼專案中偵測到的,以及一些在 Meta 內部發現的有趣錯誤。我希望這些範例能說服您嘗試在您工作的專案中啟用此規則!
no-constant-binary-expression
的作用是什麼?
此規則檢查在執行時期結果不會變化的比較運算子(==
、!==
等),以及總是或永遠不會短路的邏輯運算式(&&
、??
、||
)。
例如
+x == null
將永遠為 false,因為+
會將x
強制轉換為數字,而數字永遠不會是 nullish。{ ...foo } || DEFAULT
將永遠不會回傳DEFAULT
,因為物件永遠是 truthy。
這些都是看起來會影響程式評估方式,但實際上並不會的運算式範例。
此規則最初只是嘗試偵測不必要的 null 檢查。然而,隨著我的深入研究,我意識到無用的 null 檢查只是一個更廣泛類別的特例:無用的程式碼。最終我恍然大悟:開發人員並非有意編寫無用的程式碼,而與開發人員意圖不符的程式碼,根據定義就是錯誤。因此,您可以偵測到的任何無用程式碼都是錯誤。
當我在 Meta 的程式碼庫中執行此規則的第一個版本時,這個認知得到了證實,它偵測到各種微妙且有趣的錯誤,這些錯誤竟然通過了程式碼審查。
使用 no-constant-binary-expressions
發現的真實世界錯誤
在本節中,我將分享此規則可以捕捉到的幾種錯誤類型。每種錯誤都包含至少一個在熱門開放原始碼專案中偵測到的具體範例。我在這裡選擇包含真實範例,並非為了羞辱任何人或任何專案,而是為了強調這些錯誤是任何團隊都容易犯的。
混淆運算子優先順序
此規則發現最常見的錯誤類別是開發人員誤解運算子優先順序的地方,尤其是像 !
、+
和 typeof
等一元運算子。
if (!whitelist.has(specifier.imported.name) == null) {
return;
}
來自 Material UI(還有:VS Code 1、2、Webpack、Mozilla)
混淆 ??
和 ||
優先順序
當嘗試定義預設值時,人們會對像 a === b ?? c
這樣的運算式感到困惑,並假設它會被解析為 a === (b ?? c)
。但實際上它會被解析為 (a === b) ?? c
。
shouldShowWelcome() {
return this.viewModel?.welcomeExperience === WelcomeExperience.ForWorkspace ?? true;
}
來自 VS Code
題外話:觀察到開發人員經常被運算子優先順序搞糊塗,這啟發了我嘗試開發 一個 VS Code 擴充功能,以視覺化地釐清優先順序的解讀方式。
期望物件按值比較
來自其他結構按值比較而非按參考比較的語言的開發人員,很容易陷入認為他們可以透過與新建立的空物件進行比較來測試物件是否為空的陷阱。當然,在 JavaScript 中,物件是按參考比較的,而且沒有任何值可以等於新建立的物件字面量。
在這個範例中,hasData
將永遠設定為 true,因為 data
永遠不可能在參考上等於新建立的物件。
hasData = hasData || data !== {};
期望空物件為 false
或 null
另一個常見的 JavaScript 錯誤類別是期望空物件為 nullish 或 falsy。對於來自像 Python 這樣空列表和字典為 falsy 的語言的人來說,這可能是一個容易犯的錯誤。
const newConfigValue = { ...configProfiles } ?? {};
是 >=
還是 =>
?
我只看過一次這種特定的錯字,但我想把它包含進來,因為它是這個規則可以捕捉到的意想不到的錯誤類型的一個很好的例子。
在這裡,開發人員原本想測試一個值是否大於或等於零(>= 0
),但不小心顛倒了字元的順序,建立了一個回傳 0 && startWidth <= 1
的箭頭函式!
assert(startWidth => 0 && startWidth <= 1);
來自 Mozilla
no-constant-binary-expression
捕捉到的其他錯誤
以上五種錯誤類別並非詳盡無遺。當我最初在 Meta(非常)龐大的 monorepo 上執行此規則的第一個版本時,它發現了超過 500 個問題。雖然許多問題屬於上述類別,但也存在許多其他有趣的長尾錯誤。一些重點包括
- 認為
||
允許集合運算:states.includes('VALID' || 'IN_PROGRESS')
- 認為原始函式會傳遞 null:
Number(x) == null
- 不知道原始型別 建構子 會回傳 boxed primitives:
new Number(x) === 10
我從未打算個別為這些特定問題進行 lint 檢查,但透過簡單地嘗試識別任何「無用」的東西,我們就能夠找到並修正它們。
結論
正如您現在所看到的,no-constant-binary-expression
能夠偵測各種不同類型的錯誤。此規則之所以能做到這一點,並不是因為它被程式設計為尋找這些特定問題,而是因為所有這些錯誤都有一個共同點:它們都表現為無用的程式碼。由於開發人員通常不打算編寫無用的程式碼,因此偵測無用的程式碼通常會導致偵測到錯誤。
如果您覺得這些範例引人入勝,請考慮在您的 ESLint 設定中啟用 no-constant-binary-expression
// eslintrc
module.exports = {
rules: {
// Requires eslint >= v8.14.0
"no-constant-binary-expression": "error"
}
}
如果您這樣做了,並且它發現了錯誤,我很樂意 聽說它們!
感謝 Brad Zacher 的最初觀察,這啟發了這項工作,並建議將其作為新的核心規則提出。並感謝 Milos Djermanovic 在程式碼審查期間的重大貢獻。