Mirai Translate TECH BLOG

株式会社みらい翻訳のテックブログ

ナイスなESLintカスタムルール集

f:id:mt_nile:20210909165515p:plain

こんにちは。みらい翻訳フロントエンドエンジニアの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

ReDoSが発生する可能性のある正規表現を検出します。

検出用のライブラリとして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(=略語)の使用を禁止します。慣例的にerroreventeや、buttonbtnと訳すことが多いですが、それらの略語を使用している場合に検出します。

略語を使用していた場合、デフォルトではこの辞書に従って正しい単語に展開されます。()

// 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

switchcase節または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等の静的解析ツールを活用した、機械翻訳サービスのフロントエンド開発に興味がある方を探しています。

ご興味のある方は、ぜひ下記リンクよりご応募・お問い合わせをお待ちしております。(フロントエンドエンジニア以外も絶賛募集中です)

miraitranslate.com