Melchior

バンクーバーで働くエンジニアの備忘録

ESLintのメソッドチェインのindentの設定について

概要

ESLintのメソッドチェイン(chain)のindentの設定に問題があり3 -> 4のアップグレードができなかったのですが、なんとか苦戦してなんとかアップグレードした話です。 もっといい方法知っている方いたら教えて欲しいです、ホントに。

問題のコード

例としてarrow functionのショートハンドなどは使用していません。実際のコードはもっと大きいものを想定してください。

さて。

元々のESLintの設定はこうでした。

rules:
  indent:
    - error
    - 2

Array編

最初のchainのfunctionに複数行にまたがるArrayを渡す際、以下のコードはLintエラーになります。

// 1 (Lintエラー)
Promise.all([
  1,
  2,
  3
])
.then(value => {
  console.log(value);
});

解決方法としては、

// 2
Promise.all([
  1,
  2,
  3
])
  .then(value => {
    console.log(value);
  });

// 3
Promise
  .all([
    1,
    2,
    3
  ])
  .then(value => {
    console.log(value);
  });

// 4 (Lintエラー)
Promise.all([
    1,
    2,
    3
  ])
  .then(value => {
    console.log(value);
  });

2は気持ち悪すぎるので不採用として(Lintエラーにして欲しいレベル)、4でLintエラーにするのであれば、もはや1を採用して欲しいです。 3はアリと考えていましたが、以下の例でとてもいただけないので不採用としました。

// 5 (気持ち悪い)
_
  .chain([
    1,
    2,
    3
  })
  .map(n => {
    return n * 2;
  })
  .value();

// 6 (短いときはこう書きたい)
Promise.all([1, 2, 3])
  .then(value => {
    console.log(value);
  });

Function編

Functionのほうがもっと厄介です。

// 7 (Lintエラー)
new Promise(resolve => {
  // do something
  setTimeout(resolve);
})
.then(value => {
  console.log(value);
});

// 8 (Lintエラー)
[1, 2, 3].map(n => {
  return n * 2;
})
.filter(n => {
  return n % 2;
});

解決方法はArrayの例と同じく、

// 9
new Promise(resolve => {
  // do something
  setTimeout(resolve);
})
  .then(value => {
    console.log(value);
  });


// 10
[1, 2, 3].map(n => {
  return n * 2;
})
  .filter(n => {
    return n % 2;
  });

一つの案としては変数を定義することですが、Functionの例で毎度変数を作らないといけなくなるため不採用にしました。

解決方法

IndentにMemberExpressionというオプションがあり、chain時のindentを定義できます。 上記の例を実現したいとき、chainのindentが0だったり2だったりするため、offにすることで回避することができました。

rules:
  indent:
    - error
    - 2
    - MemberExpression: off

まとめ

ESLintのバージョンをアップデートすることで、多くの機能が追加されているのでもっと厳しくチェックしていただけるようになりました。助かります、ありがとうございます。

がしかし、果たしてこれは良いコードなのか…という疑問も残ります。

例えば、

// 11
const obj = {};
const array = _.chain([
  1,
  2,
  3,
]).map(n => {
  return n * 2;
})
.filter(n => {
  return n % 2;
});
obj.a = 1;

この例の場合、arrayのchainがどこで終わりかわかりづらいため、その後に続く行が読みづらい可能性が高いです。

// 12
const obj = {};
const array = _
  .chain([
    1, 
    2, 
    3
  ])
  .map(n => {
    return n * 2;
  })
  .filter(n => {
    return n % 2;
  });
obj.a = 1;

そういう意味ではこう書いたほうが読みやすい可能性も…

リーダブルコードって難しいですね。

リンク