こんにちは。みらい翻訳フロントエンドエンジニアのWillと申します。
今回は、公開されているESLintのカスタムルールを集めて、個人的に有用そうだ、面白そうだ、と思ったカスタムルールを紹介します。
@shopify/eslint-pluginより
eコマースプラットフォームを提供している、Shopifyが公開しているESLintの設定に含まれているルールから見ていきます。
jsx-no-hardcoded-content
JSXで文字列を直接ハードコーディングすることを禁止するルールです。
多言語表示対応するため、i18nライブラリを介してテキストを表示することが必須であるプロジェクトで、うっかりハードコードしていないか検出したいときに有用です。
// 以下はNG <div>Content</div> <MyComponent>{someContent()}{` and more content`}</MyComponent>
prefer-early-return
早期リターンで書ける場合を検出します。ネストが1段減らせるため、コールドリーディング時の負荷を減らすことに繋がるかもしれません。
// NG function foo() { if (a) { b(); c(); } } // OK function foo() { if (!a) { return; } b(); c(); }
prefer-module-scope-constants
スクリーミングスネークケース(DEFAULT_VALUE
のような大文字のスネークケース)は、const
による宣言かつ、モジュールスコープしか許さないというルールです。
// NG let DEFAULT_VALUE = 0; // OK const DEFAULT_VALUE = 0; // NG function myFunction() { const OTHER_VALUE = false; }
react-hooks-strict-return
カスタムフックの戻り値がタプルである場合、その個数を2個までに制限するルールです。2個以上を返却したい場合は、キー名があるオブジェクトに入れることを強制することができます。
// NG function useFoo() { const bar = 'bar'; const baz = 'baz'; const qux = 'qux' return [bar, baz, qux]; } // OK function useFoo() { const bar = 'bar'; const baz = 'baz'; const qux = 'qux'; return { bar, baz, qux, }; }
eslint-plugin-unicornより
次に、eslint-plugin-unicorn
に収録されているルールを見ていきます。
better-regex
正規表現でキャラクタークラスに置換できる表現を探してくれます。
// before fix const regex = /[0-9]/; // after fix const regex = /\d/;
no-unsafe-regex
検出用のライブラリとしてsubstack/safe-regexが使われているようです。
// NG const foo = /(x+x+)+y/;
filename-case
Lint対象になっているファイル自体の名前のケーススタイルを強制するルールです。
こういったファイル名に関するルールは手動レビューに頼りがちで、いつのまにかいろんなケースで書かれたファイル名が散乱しがちと思います。このルールでLintによる機械的な縛りが出来るのはGOODですね。
number-literal-case
数値リテラルの16進数表記で出るラテン文字のケースを揃えたり、指数を表すe
のケースを揃えてくれます。(fix対応)
// before fix const foo = 0Xff; const foo = 2E+5; // after fix const foo = 0xFF; const foo = 2e+5;
numeric-separators-style
ES2021でリリースされたNumeric Separatorsを3桁区切りで自動挿入してくれます。
// before fix const foo = 1000000; // after fix const foo = 1_000_000;
prefer-array-〇〇系
比較的新しいメソッドである、find()
flat()
some()
等でシンプルに書ける場合に教えてくれます。
// NG const item = array.filter(x => isUnicorn(x))[0]; // OK const item = array.find(x => isUnicorn(x)); // NG const foo = array.reduce((a, b) => a.concat(b), []); // OK const foo = array.flat(); // NG const hasUnicorn = array.filter(element => isUnicorn(element)).length > 0; // OK const hasUnicorn = array.some(element => isUnicorn(element));
prefer-includes
対象の存在自体を確認したいだけの場合、比較的新しいメソッドであるincludes()
が適しています。indexOf()
やsome()
を利用した存在チェックを行っている場合、教えてくれます。
// NG str.indexOf('foo') > -1; // OK str.includes('foo');
prefer-switch
if-else
による"複雑な"条件分岐を、switch
で書き換え可能な場合に教えてくれます。
デフォルトでは、条件が2ケース以下の場合は無視されます。
// NG if (foo === 1) { } else if (foo === 2) { } else if (foo === 3) { } else { } // OK switch (foo) { case 1: { break; } case 2: { break; } case 3: { break; } default: { } }
prevent-abbreviations
abbreviations(=略語)の使用を禁止します。慣例的にerror
やevent
をe
や、button
をbtn
と訳すことが多いですが、それらの略語を使用している場合に検出します。
略語を使用していた場合、デフォルトではこの辞書に従って正しい単語に展開されます。()
// NG const val = 0; const elem = document.querySelector('#main') // OK const value = 0; const element = document.querySelector('#main')
eslint-plugin-sonarjsより
次に、SonarQubeなどのコード品質管理ツールを出しているSonarSourceが公開しているルールを見ていきます。
cognitive-complexity
Cognitive Complexity(認知的複雑性)を計測出来るルールです。
よく似た指標として、Cyclomatic Complexity(循環的複雑度)がありますが、Cognitive Complexityは、より人間の認知に近い感覚で複雑さを数値として表すことが出来るようです。
- ちなみに、Cyclomatic ComplexityはESLint標準のcomplexityルールで計測することができます。
このルールを適用することで、一定以上のCognitive Complexityがあるコードを検出した場合、警告を出すことが出来ます。リファクタリングを行うべき箇所を見つけ出すのに役立ちますね。
no-duplicate-string
内容が重複した文字列リテラルを検出します。デフォルトでは3以上の同一内容の文字列リテラルがあれば警告します。
// NG showAlert('Mirai Translator'); ... showToast('Mirai Translator'); ... showMessage('Mirai Translator'); // OK const BRAND_NAME = 'Mirai Translator' ... showAlert(BRAND_NAME); ... showToast(BRAND_NAME); ... showMessage(BRAND_NAME);
no-duplicated-branches
switch
のcase
節またはif-else
によるチェーンの各節において、コードの重複があれば警告します。
// NG if (foo < 3) { // ここと a(); b(); c(); } else (foo >= 3 && bar === 0) { a(); } else (bar === 1) { // ここは同じ a(); b(); c(); } else { b(); } // OK if (foo < 3 || bar === 1) { a(); b(); c(); } else (foo >= 3 && bar === 0) { a(); } else { b(); }
prefer-immediate-return
即座にreturn
する場合、変数に格納することを禁止します。
// NG function fetchSomething() { const responseFromServer = api.get({foo: 'bar'}) return responseFromServer } // OK function fetchSomething() { return api.get({foo: 'bar'}) }
ちなみに、ルールの説明によると、修正前のプラクティスを「説明変数として機能し、可読性を高める効果がある」と解釈するのではなく、メソッド名にて何を返却するのか明示するのが良いプラクティスだ、と書かれています。
prefer-single-boolean-return
if-else
によりBooleanリテラルをreturn
するコードを書いた時、単に式を返すように警告します。
// NG if (expression) { return true; } else { return false; } // OK return expression;
おわりに
様々なルールを見てきましが、いかがだったでしょうか。今回紹介しきれなかったルールも多数あります。興味がある方は各リポジトリよりご参照ください。
ESLintに標準搭載されているルールも有用ですが、上記で紹介したようなカスタムルールを導入することで、各開発者がより可読性の高く、安全なプログラムを書ける上、コードレビュー時の負担軽減にも繋がります。
また、これらのルールはコードを書く上でのエッセンス・ベストプラクティスが詰まっており、Lintによる指摘を通じて自身のスキルの向上に役立てることができます。
チームでプロジェクトに導入する・しないを検討するのも良いですが、まずローカルにインストールして個人で試してみるのもおすすめです。
それでは、また次の記事でお会いしましょう。
みらい翻訳では、フロントエンドエンジニアを募集しています!
ESLint等の静的解析ツールを活用した、機械翻訳サービスのフロントエンド開発に興味がある方を探しています。
ご興味のある方は、ぜひ下記リンクよりご応募・お問い合わせをお待ちしております。(フロントエンドエンジニア以外も絶賛募集中です)