2021年10月24日日曜日

typescript-eslintでインデントのルールがおかしいときの対応

現在TypeScriptで作っているウェブアプリケーションがあるんですが、先日ESLintのバージョンを7系から8系に、typescript-eslintのバージョンを4系から5系に上げたところ、今まで出なかったエラーが出てきました。

問題を指摘されたのは以下のようなコード片です。

class Foo {
	@decorator
	bar: number;
}

一見問題なさそうに見えます。実際文法的には問題ありませんし今までは特に指摘されてきませんでした(ここだけ見るとbarが初期化されていませんが、あくまでコード片です。実際にはコンストラクターで値を割り当てています)。

何がアカンのかというと、barのインデントだそうです。こうしろと。

class Foo {
	@decorator
		bar: number; // ←こうしないとエラー
}

えっ。こんなコーディングスタイル見たことない

新しいルールが追加されたのかと思って、これを無効にするオプションを探してみたんですが特に見つからず。

抽象構文木ノードを指定してチェックを回避

新しいルールのようなものは見当たらなかったんですが、ESLintでは特定の抽象構文木ノードに対してindentルールを無効化できるようなので、これを使うことにしました。

AST Explorerで抽象構文木を確認してみると、barはClassBodyClassPropertyIdentifierと辿れるようなので、.eslintrc.ymlindentルールを以下のように設定。

indent:
- error
- ignoredNodes:
  - "ClassBody > ClassProperty > Identifier"

これで一見落着・・・とはならなかった

もう一度lintを走らせても同じエラーが出てきます。抽象構文木のノード指定自体は間違っていないはずなのになぁ・・・と思いながら色々試してみたところ、以下のような指定ではエラーにならないことがわかりました。

  • Identifierのみ
  • ClassBody Identifier
  • ClassBody > * > Identifier

この中で一番制限のきつい一番下を採用してもいいんですが、これだとメソッド定義部分のインデントがおかしくてもエラーが出なくなってしまうので、もうちょっとなんとかしたい。何よりなぜClassPropertyを指定できないのかわからないまま終えるのはキモい

問題は@typescript-eslint/parserのバージョンだった

この問題にしばらく悩まされたあとでふと目に入ったのが、AST Explorerの右上に表示されているパーサーのバージョン。


ちょっと古くね?そもそも今回問題になってるのはバージョン5なので、パーサーのバージョンも合わせないと。

そこで手元に最新のバージョンを用意して同じコードを解析した結果、バージョン5ではClassPropertyの部分がPropertyDefinitionに変わっていました。

というわけで、ルールを以下のように設定して今度こそ解決。

indent:
- error
- ignoredNodes:
  - "ClassBody > PropertyDefinition[decorators] > Identifier"

無視するノードはデコレーターがついているプロパティーに限定したいので、[decorators]をつけています。

今作っているアプリケーションはメソッドにまではデコレーターをつけていないのですが、もしつけるとしたら以下のように変更することになると思います。試してませんが。

indent:
- error
- ignoredNodes:
  - "ClassBody > PropertyDefinition[decorators] > Identifier"
  - "ClassBody > MethodDefinition[decorators] > Identifier"

余談

同様のissueが数日前に作成されていました。これによると、インデント関連の問題はすでにtypescript-eslintチームは認識していて、要するにいろいろバギーで直したいけどみんな手が回らなくて誰かに音頭取ってほしいけどコードが複雑だそうです。

よっしゃワイがなんとかしたる!と言いたいところですが名乗りを上げる度胸なんぞあるわけもなく、このブログでワークアラウンドを紹介するのが精一杯でした。みなさんのお役に立てますように。

0 件のコメント:

コメントを投稿