ESLintで開発環境をエンチャント

フロントエンドエンジニアの渡辺です。
業務委託で働いてる身ですが記事書くことになりました。懐深い。

先日「ESLintのルール設定を全て確認して設定をする」ということをしました。
(ESLintはv4.0.0時点で240以上の設定項目があります。)

普段はルール設定が膨大なので必要な項目しか見ないのですが、
全てに目を通してみると得られた知見が多くあったので共有します。

設定方針

まず、ESLintの膨大なルール設定をどう設定するかを決めます。
やり方は大体3パターンに分類できます。

  • 緩いルール設定(eslint:recommendedなど)を元に、設定を足して厳しくする。
  • 厳しいルール設定(airbnbなど)を元に、設定を引いて緩くする。
  • 既存のソースコードを解析し、ESLintの機能でルール設定を自動生成する。

今回は「入れたいルールに絞って設定したい」という思いがあり、
eslint:recommended を元にルール設定を付け足す方法にしました。

ESLintのルール内容

ESLintのルール244項目のうち、eslint:recommendedは47項目設定されています。
このeslint:recommendedを元に、21項目のルール設定を追加しました。
この追加したルール設定について、一部を紹介していきます。
ESLintのルールはジャンル分けされているので、ジャンルごとに見ていきます。
また --fix オプションで自動修正できるものは(fixable)をつけておきました。

1. Possible Errors

Possible Errorsは、構文やロジックのエラーに関連した設定です。

no-prototype-builtins

これは以下のコードでエラーになるのを防いでくれます。

const foo = Object.create(null);
foo.hasOwnProperty('bar');

このコードがなぜエラーになるかというと Object.create(null) で作ったオブジェクトはObject型にもかかわらず、Object.prototypeのメソッドを持たないからです。
そのため Uncaught TypeError: foo.hasOwnProperty is not a function という例外を吐いて落ちてしまいます。

Object.prototypeのメソッドをインスタンスから呼び出すのは禁止し、
代わりにObject.prototypeをcallする書き方で対応します。

const foo = Object.create(null);
Object.prototype.hasOwnProperty.call(foo, 'bar');

以前このエラーにハマったことがあったので、感激しながら秒速で設定しました。
ちなみに、そのときハマったコードというのがこちら↓
https://github.com/expressjs/cookie-parser/blob/1.4.3/index.js#L51

2. Best Practices

Best Practicesは、問題を回避するのに役立つ設定がまとめられています。

no-else-return (fixable)

elseブロック内でreturnしている場合、そのelseは省略できるのでエラーにします。
なお eslint --fix で自動的に修正することができます。すごい……

// error
if (x) {
  return y;
} else {
  return z;
}
// ok
if (x) {
  return y;
}
return z;

経験的に「無駄にelse内でreturnしているとコードが複雑化しやすい」と
感じていて、ESLintでたまたま禁止できたので設定しました。

radix

parseInt() を使うとき、第二引数を強制します。
ES5以前のparseIntでは8進数を自動検出するようになっており、
parseInt("071"); を実行すると 57 となってバグ原因になるのでエラーにします。

みなさんは知っていましたか?私は知りませんでした……
ただし、最近のブラウザであれば 71 になるので今は不要かもしれません。

3. Stylistic Issues

Stylistic Issuesでは、コードのスタイルに関連する設定がまとめられています。
「ただのスタイルでしょ?趣味のルール設定なのでは?」と思うかもしれませんが、
意外に重要な設定もあったので紹介したいと思います。

comma-dangle (fixable)

いわゆるケツカンマを強制します。
例えば、

{
  bar: 0
}

{
  bar: 0,
  baz: 1
}

のように変更してコミットすると baz: 1 しか意味的な差分がないのに、
bar: 0,baz: 1 の2行にコード差分が出てしまいます。
そうするとコードレビューしずらかったり、コンフリクトの原因にもなるので
ケツカンマ必須に設定しました。

eol-last (fixable)

ファイルの最後に必ず改行を入れるようにします。
これもgit差分を増やさないため設定しました。

max-depth

ブロックのネスト数によってエラーにしてくれます。
以下のコードはネスト数5です。こんなコード見たくありませんよね。

for (;;) {
  let val = () => (param) => {
    if (true) {
      if (true) { // ネスト4
        if (true) { // ネスト5
        }
      }
    }
  };
}

速攻でエラー設定にしました。
とはいえ設定は緩めで、4までネストOKにしています。
「3で警告して4以上はエラー」のような設定ができないので緩くしました。

max-nested-callbacks

コールバックのネスト数によってエラーにしてくれます。
以下のコードはネスト数4です。やはりこんなコード見たくない。

foo1(function() {
  foo2(function() {
    foo3(function() { // ネスト3
      foo4(function() { // ネスト4
      });
    });
  });
});

とはいえ、これも max-depth と同じ理由で少し緩めの3に設定しています。

no-lonely-if (fixable)

以下のような無駄なifのネストをエラーにしてくれます。
これもeslint --fix で自動修正が可能です。すごい……

// error
if (foo) {
  // ...
} else {
  if (bar) {
    // ...
  }
}
// ok
if (foo) {
  // ...
} else if (bar) {
  // ...
}

これも no-else-return と同じで、前から禁止したいと思っていました。
こんなコードを書いてしまうときは追い詰められて頭がボケているときなので、
速攻でエラー設定にしました。

no-tabs

タブによるインデントは本当に嫌いなので禁止にしました。例えば

const foo = {
  bar: 0,
  x  : 1, // xと:の間がタブ文字
};

のようなコードがあった場合、他の人の環境では表示崩れを起こすかもしれません。

const foo = {
  bar: 0,
  x     : 1,
};

こんな風に崩れたのを見た日には、喧嘩が起きても不思議ではありません。
そのため最初からエラーにしておくと平和が保たれます。

4. ECMAScript 6

ECMAScript 6は、ES2015以降の構文に関する設定がまとまっています。

prefer-const (fixable)

constで問題無いのにも関わらずletを使っているとエラーにしてくれます。
例えば、

let a = 3;
console.log(a);
return

わざわざletを使っているということは、どこかで再代入していると予測できるので、
再代入箇所がないか気をつける必要がでてしまいます。
これも eslint --fix で自動修正できて便利なので、エラーに設定しました。

prefer-rest-params

これは、暗黙の変数であるargumentsの使用を禁止します。
暗黙の変数自体使うのを避けたいですし、ES2015であればrest parametersで代用できますし、argumentsはArray.prototypeのメソッドを使えないので少し不便です。
わざわざargumentsを使うメリットが見当たらないのでエラーにしました。

// error
function foo() {
  console.log(arguments);
}
function bar(a, b) {
  console.log(a, b, arguments);
}
// OK
function foo(...args) {
  console.log(args);
}
function bar(...args) {
  const {a, b} = args;
  console.log(a, b, args);
}

設定しなかったルール

設定しなかったものや、検討に悩んだものもあるので紹介します。

セミコロンなし

セミコロン有り派が多いのでセミコロン必須で設定しました。
セミコロンがないと意図しないバグやエラーの原因になるからです。
ただ、ESLintを見ていくと、意図しないセミコロン抜けがある場合にエラーを出すルールがありました。

  • no-unexpected-multiline

これを設定すると、例えばセミコロンなしで設定しても以下のように警告してくれます

/* eslint semi: ["error", "never"] */
/* eslint no-unexpected-multiline: "error" */

const foo = {} // ここはセミコロン無しだと警告がでる
(function() {
  return 1 // ここはセミコロン有りだと警告がでる
})()

す、すごい……
私はセミコロン有り派でしたが食わず嫌いをしていました。
これならセミコロン無しでもいいかな、という気になっています。

閾値が難しいもの

「コードの複雑さでエラーにする」という所で、
max-depthmax-nested-callbacks と似たような以下の設定があります。
これらはどこからエラーにするか判断が難しいので設定しないことにしました。

ルール 内容
complexity 複雑度が設定を超えるとエラー
max-len 1行の長さが設定を超えるとエラー
max-lines 1ファイルのステップ数が設定を超えるとエラー
max-params 引数の数が設定を超えるとエラー
max-statements 変数の数が設定を超えるとエラー
max-statements-per-line 1行に存在する変数の数が設定を超えるとエラー

迷ったもの

入れるか迷った場合は入れないようにしました。
ほとんどのルール設定で入れるか迷った部分が多かったんですが、
特に迷ったのがこちらです。

ルール 内容
valid-jsdoc 有効なJSDocコメントかチェックする
no-eq-null ==, != によるnull比較を禁止する
no-magic-numbers マジックナンバーを禁止する
no-return-await return文にawaitを使うことを禁止する
no-throw-literal 例外として投げられるものを制限する
no-warning-comments TODOやFIXEME等の警告コメントを禁止する
prefer-promise-reject-errors Promiseのrejectに渡せるものを制限する
require-await awaitを使っていないasync関数を禁止する
yoda yoda("red"===colorのような左右を逆にすべき)条件を禁止する
consistent-this self = this のようにthisを代入する変数名を統一する
require-jsdoc JSDocコメントを必須にする
unicode-bom BOMを必須または禁止する
no-duplicate-imports 重複したモジュールのimportを禁止する
no-var varの使用を禁止する

Node.js and CommonJS

ESLintにはNode.js用のルール設定があります。
今回はwebpackなどの設定にしかNode.jsのコードがないので入れませんでした。

Node.jsのコードを開発で使う場合は設定しておくと助けになると思います。

ESLintの全てルールを確認してみて

どうだったでしょうか。
意外と知らないことが結構あったのではないかと思います。
私としてはいくつか大きな収穫がありました

1. ESLintに怒られても動じない

ESLintに怒られてもすぐ意味がわかるようになったので、
怒られるストレスがほとんどなくなりました。

2. 厳しい設定から緩くするのもアリ

自分が把握していないルール設定は使いたくないという気持ちがあったんですが、
ESLintのルール設定は有用なものがほとんどだと分かったので、

  • 厳しいルール設定(airbnbなど)を元に、設定を引いて緩くする。

この方法で設定するのもアリだと自信を持って言えるようになりました。

3. コード品質への貢献度が想像以上

コードのスタイルに関することがメインだと思っていましたが、
複雑度チェックやネスト数のチェック、無意味なelseのチェックなど、
コードの品質を直接あげる機能が思った以上に優秀でした。
経験の浅いメンバーにとって良いチェックツールになります。

4. 自動修正で時短コーディング

どこまで--fixで自動修正できるか把握できるようになったので、
自動修正できるものは自動修正に任せるようになりました。
スペースやセミコロンを抜いて書けるので、コーディング時間の短縮になりました。

最後に

メリットばかりのESLint。
全部の設定を確認するのは大変ですが、
一度確認しておくことをオススメします。
 
 
マネーフォワードでは、様々な雇用形態でエンジニアを募集しています。
ご応募お待ちしています。

【採用サイト】
マネーフォワード採用サイト
Wantedly | マネーフォワード

【プロダクト一覧】
自動家計簿・資産管理サービス『マネーフォワード』
Web
iPhone,iPad
Android

ビジネス向けクラウドサービス『MFクラウドシリーズ』
会計ソフト『MFクラウド会計』
確定申告ソフト『MFクラウド確定申告』
請求書管理ソフト『MFクラウド請求書』
給与計算ソフト『MFクラウド給与』
経費精算ソフト『MFクラウド経費』
入金消込ソフト『MFクラウド消込』
マイナンバー管理ソフト『MFクラウドマイナンバー』
資金調達サービス『MFクラウドファイナンス』

メディア
くらしの経済メディア『MONEY PLUS』

Pocket