則詳解:掌控三元表達(dá)式換行風(fēng)格)
ESLint multiline-ternary 規(guī)則詳解掌控三元表達(dá)式換行風(fēng)格【免費(fèi)下載鏈接】eslintFind and fix problems in your JavaScript code.項目地址: https://gitcode.com/GitHub_Trending/es/eslint三元表達(dá)式ternary expression是 JavaScript 中最常用的條件表達(dá)式之一而它的換行風(fēng)格直接影響代碼的可讀性。ESLint 內(nèi)置的multiline-ternary規(guī)則rule_type 為layout專門用于強(qiáng)制或禁止三元表達(dá)式操作數(shù)之間的換行幫助你建立統(tǒng)一、可維護(hù)的寫法規(guī)范。本文以 ESLint 倉庫中的規(guī)則文檔 docs/src/rules/multiline-ternary.md 為主體結(jié)合規(guī)則實現(xiàn)源碼 lib/rules/multiline-ternary.js 與測試用例 tests/lib/rules/multiline-ternary.js完整講解該規(guī)則的三種配置模式、可自動修復(fù)特性、與operator-linebreak規(guī)則的配合方式以及何時應(yīng)當(dāng)關(guān)閉它。為什么需要規(guī)范三元表達(dá)式的換行JavaScript 語法上允許三元表達(dá)式的操作數(shù)之間出現(xiàn)換行合理利用換行可以顯著提升程序的可讀性。例如下面這行代碼把所有操作數(shù)擠在一行var foo bar baz ? value1 : value2;可以改寫為以下任意一種多行形式讓各操作數(shù)test、consequent、alternate在視覺上更清晰地區(qū)分開var foo bar baz ? value1 : value2; var foo bar baz ? value1 : value2;第二種寫法運(yùn)算符?與:行首對齊是社區(qū)中更常見的風(fēng)格也是operator-linebreak規(guī)則默認(rèn)配置所推薦的寫法。multiline-ternary規(guī)則的價值在于它不關(guān)心你采用哪種多行排布只約束是否換行從而讓團(tuán)隊代碼保持一致。規(guī)則詳情與三種配置模式該規(guī)則接受一個字符串選項共有三個取值選項行為always默認(rèn)強(qiáng)制三元表達(dá)式的操作數(shù)之間必須有換行always-multiline僅當(dāng)表達(dá)式本身跨越多行時強(qiáng)制操作數(shù)之間換行never禁止操作數(shù)之間出現(xiàn)換行從源碼 lib/rules/multiline-ternary.js 可以看到三個選項在實現(xiàn)中的實際含義const option context.options[0]; const multiline option ! never; const allowSingleLine option always-multiline;即always與always-multiline共享同一套要求換行的檢查邏輯區(qū)別僅在于always-multiline額外允許整個表達(dá)式保持單行allowSingleLine為true時若node.loc.start.line node.loc.end.line則直接跳過檢查。需要特別注意的是本規(guī)則不約束?與:運(yùn)算符本身的位置。如果你還想強(qiáng)制運(yùn)算符換行位置行尾還是行首需要配合 operator-linebreak 規(guī)則使用后文會詳細(xì)說明。always默認(rèn)模式always是默認(rèn)選項要求三元表達(dá)式的 test、consequent、alternate 三個操作數(shù)之間必須換行。使用always時的錯誤代碼示例/*eslint multiline-ternary: [error, always]*/ foo bar ? value1 : value2; foo bar ? value : value2; foo bar ? value : value2;以上三種寫法分別違反了test 與 consequent 之間換行或consequent 與 alternate 之間換行的約定。使用always時的正確代碼示例/*eslint multiline-ternary: [error, always]*/ foo bar ? value1 : value2; foo bar ? (baz qux ? value1 : value2) : value3; foo bar ? (baz qux ? value1 : value2) : value3;注意后兩個示例展示了一個關(guān)鍵行為嵌套的三元表達(dá)式同樣適用該規(guī)則。只要表達(dá)式整體跨行每一層三元操作數(shù)之間都必須換行。ConditionalExpression訪問器在遇到嵌套表達(dá)式時會為每一層分別觸發(fā)檢查。always-multilinealways-multiline是更溫和的模式單行三元表達(dá)式完全合法但一旦表達(dá)式跨越多行操作數(shù)之間就必須換行。使用always-multiline時的錯誤代碼示例/*eslint multiline-ternary: [error, always-multiline]*/ foo bar ? value1 : value2; foo bar ? value1 : value2; foo bar bar baz ? value1 : value2;前兩例屬于表達(dá)式已跨行但操作數(shù)之間未換行第三例雖然條件部分test自身是多行的但整個三元表達(dá)式的操作數(shù)都位于同一行同樣違規(guī)。使用always-multiline時的正確代碼示例/*eslint multiline-ternary: [error, always-multiline]*/ foo bar ? value1 : value2; foo bar ? value1 : value2; foo bar ? (baz qux ? value1 : value2) : value3; foo bar ? (baz qux ? value1 : value2) : value3; foo bar bar baz ? value1 : value2; foo bar ? baz qux ? value1 : value2 : value3;從這些正確示例可以看出always-multiline的判定粒度判斷依據(jù)是整個三元表達(dá)式節(jié)點的起止行號node.loc.start.line node.loc.end.line而非操作數(shù)內(nèi)部是否多行。因此foo bar \n bar baz ? value1 : value2;這種條件本身跨行但三元表達(dá)式收尾在同一行的寫法是違規(guī)的而把?、:和 consequent 換行后則合法同理(baz qux ? value1 : value2)作為子表達(dá)式整體位于一行時完全允許因為內(nèi)層三元表達(dá)式自身沒有跨行。nevernever是嚴(yán)格風(fēng)格禁止操作數(shù)之間出現(xiàn)任何換行整個三元表達(dá)式必須保持單行允許操作數(shù)自身內(nèi)部換行例如括號內(nèi)的內(nèi)容。使用never時的錯誤代碼示例/*eslint multiline-ternary: [error, never]*/ foo bar ? value : value2; foo bar ? value : value2; foo bar ? value1 : value2;前兩例分別在 consequent 與 alternate 之間、test 與 consequent 之間出現(xiàn)了換行第三例在 test 內(nèi)部換行后緊跟?換行同樣違規(guī)。使用never時的正確代碼示例/*eslint multiline-ternary: [error, never]*/ foo bar ? value1 : value2; foo bar ? (baz qux ? value1 : value2) : value3; foo bar ? ( baz qux ? value1 : value2 ) : value3;注意第三例consequent 是一對括號包裹的多行表達(dá)式但操作數(shù)之間沒有換行因此合法。測試用例 tests/lib/rules/multiline-ternary.js 中never模式的合法用例如(a\n) ? b : c、a ? (\nb) : c也印證了這一點——換行發(fā)生在操作數(shù)內(nèi)部括號內(nèi)時不觸發(fā)規(guī)則。在項目中啟用規(guī)則multiline-ternary不是eslint:recommended中的默認(rèn)規(guī)則docs.recommended為false見 lib/rules/multiline-ternary.js需要顯式配置才會生效。在 flat configeslint.config.js中的典型寫法export default [ { rules: { multiline-ternary: [error, always] } } ];將選項替換為always-multiline或never即可切換模式[warn, always]則將該規(guī)則的違規(guī)降級為警告。該規(guī)則注冊于 lib/rules/index.js可通過規(guī)則名multiline-ternary直接引用。自動修復(fù)行為與源碼實現(xiàn)解析該規(guī)則可自動修復(fù)meta.fixable: whitespace見 lib/rules/multiline-ternary.js即使用eslint --fix可以自動調(diào)整三元表達(dá)式的換行。從實現(xiàn)看規(guī)則監(jiān)聽ConditionalExpression節(jié)點lib/rules/multiline-ternary.js核心邏輯是通過sourceCode.getTokenAfter定位?questionToken和:colonToken其中astUtils.isNotClosingParenToken用于跳過 test 或 consequent 結(jié)尾的右括號正確處理(a)\n? b\n: c這類帶括號的寫法分別取 test 的末 token、consequent 的首末 token、alternate 的首 token用astUtils.isTokenOnSameLine判斷?與:兩側(cè)是否同行根據(jù)選項決定是報告expectedTestCons/expectedConsAlt期望換行還是unexpectedTestCons/unexpectedConsAlt不應(yīng)換行。在要求換行模式下autofix 會在缺失換行的位置插入\n通過fixer.replaceTextRange將空白區(qū)間替換為換行符在never模式下autofix 會刪除多余換行通過fixer.removeRange移除跨行空白。一個重要的邊界行為當(dāng)三元表達(dá)式內(nèi)部存在注釋時規(guī)則不會執(zhí)行自動修復(fù)。源碼通過sourceCode.getCommentsInside(node)檢測注釋const hasComments !!sourceCode.getCommentsInside(node).length;一旦hasComments為true所有 fix 函數(shù)直接返回null僅報告錯誤而不做任何自動修改。測試用例對此有專門驗證tests/lib/rules/multiline-ternary.js// 報告 expectedConsAlt 錯誤但 output 為 null不自動修復(fù) code: a ? // comment\nb : c;, output: null,這一設(shè)計是為了避免自動重排時破壞注釋與操作數(shù)的對應(yīng)關(guān)系。自動修復(fù)會生成多條消息。例如在always模式下a ? b : c會被同時報告expectedTestCons與expectedConsAlt兩條錯誤并修復(fù)為a\n? b\n: c見 tests/lib/rules/multiline-ternary.js嵌套表達(dá)式a ? (b ? c : d) : e則會為內(nèi)層三元表達(dá)式再報告兩條錯誤一次性修復(fù)為多層換行結(jié)構(gòu)。與 operator-linebreak 規(guī)則協(xié)同使用官方文檔明確指出multiline-ternary只約束是否換行不約束運(yùn)算符換行位置。如果你希望同時規(guī)范?和:的位置需要啟用 operator-linebreak 規(guī)則。operator-linebreak的默認(rèn)配置是after, { overrides: { ?: before, :: before } }——即大多數(shù)運(yùn)算符換行放在運(yùn)算符之后但三元運(yùn)算符?和:特例為換行放在運(yùn)算符之前。這與multiline-ternary的always模式結(jié)合可以強(qiáng)制出如下行首對齊的經(jīng)典風(fēng)格answer everything ? 42 : foo;如果團(tuán)隊更喜歡行尾風(fēng)格運(yùn)算符放在行尾則可以為operator-linebreak顯式配置export default [ { rules: { multiline-ternary: [error, always], operator-linebreak: [error, after, { overrides: { ?: after, :: after } }] } } ];兩個規(guī)則職責(zé)互補(bǔ)multiline-ternary負(fù)責(zé)三操作數(shù)兩兩不同行operator-linebreak負(fù)責(zé)運(yùn)算符在行首還是行尾配合使用即可完全鎖定三元表達(dá)式的排版風(fēng)格。何時不使用此規(guī)則如果你所在的項目對三元表達(dá)式操作數(shù)是否換行沒有嚴(yán)格的統(tǒng)一約定例如已經(jīng)通過 Prettier 等格式化工具管理排版或團(tuán)隊習(xí)慣單行三元表達(dá)式且不需要強(qiáng)制可以安全地關(guān)閉此規(guī)則。關(guān)閉方式很簡單不在eslint.config.js中配置該規(guī)則或?qū)⒁?guī)則值設(shè)為off。棄用狀態(tài)與遷移說明從源碼頭部的 deprecated 元數(shù)據(jù)lib/rules/multiline-ternary.js可以確認(rèn)該規(guī)則自 ESLint v8.53.0 起被棄用可用至 v11.0.0理由是格式化類規(guī)則正被移出 ESLint 核心。被棄用的格式化規(guī)則由stylistic/eslint-plugin接手維護(hù)其中multiline-ternary的等價規(guī)則位于該插件的同名規(guī)則中。如果你的 ESLint 版本已高于 v8.53.0建議在遷移期內(nèi)逐步從 ESLint 核心規(guī)則切換到stylistic/eslint-plugin提供的同名規(guī)則以保證在 v11.0.0 之后仍能獲得相同的檢查能力。兼容性該規(guī)則源自 JSCS 的requireMultiLineTernary規(guī)則參見原文檔 Compatibility 一節(jié)行為上與其保持兼容即強(qiáng)制三元表達(dá)式操作數(shù)換行。如果你正在從 JSCS 遷移到 ESLint可以在遷移映射表中將 JSCS 的該規(guī)則直接映射為multiline-ternary: [error, always]。小結(jié)multiline-ternary是 ESLint 布局類規(guī)則中對三元表達(dá)式排版約束最直接的規(guī)則三個選項覆蓋了從強(qiáng)制多行到強(qiáng)制單行的完整光譜并支持--fix自動修復(fù)注釋存在時除外。理解其只管換行、不管運(yùn)算符位置的邊界配合operator-linebreak規(guī)則即可構(gòu)建完整、可自動化的三元表達(dá)式排版方案。需要注意的是其棄用狀態(tài)在新項目中建議直接使用stylistic/eslint-plugin提供的等價規(guī)則。【免費(fèi)下載鏈接】eslintFind and fix problems in your JavaScript code.項目地址: https://gitcode.com/GitHub_Trending/es/eslint創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考