VSCodeにおけるPrettierとESLint(TSLint)の理解

背景

こんにちは、Naoyaです。Angularを採用したプロジェクトで、VSCodeにてTypeScriptを用いて開発することになり、フォーマッタ等を調べ、環境を作りました。しかし思ったより複雑で分かりづらかったため、自分の理解も兼ねて記事にしました。

対象読者

  • そもそもLintとかフォーマッタとかPrettierがよく分かってない、各々の言葉の次元が怪しい人
  • VSCodeにてTypeScriptを用いて開発するときのLintやフォーマッタをいい感じに設定したい人(特にAngularを使っている人向け)

といっても環境構築に関しては簡単にしか触れません。詳しいのがたくさんあるので。あくまで立ち位置の理解等がメイン。

登場人物を調べる

よく聞く奴らの立ち位置を知りたく、調べた。

Prettierとは?

Prettier公式 github

  • フォーマットするためのツールである
  • つまり、Prettierというツールをソースコードに対して実行するとソースコードが変更され、無駄な改行が消されたり、インデントが整えられたりするなどコードの見た目がよくなる
  • Prettierではどのようにコードを整形するかをオプションごとに設定できる。
  • Prettierはあくまでソースコードを整形するためのツールであり、「この書き方だとパフォーマンスが悪いから直せ!」といったことは言わない

ESLintとは?

ESLint公式 github

  • lintツールの1つ。lintツールとは、コードの厳密なチェックを行うプログラムであり、静的解析ツールとも言われる
  • ESLintはECMAScriptやJavaScriptのLintということ
  • フォーマッタとは違い、そもそもはコードを修正することが前提のツールではない。あくまで解析してここが危ないよ!って言ってくれるツール
  • どういうものを「危ないよ!」って言うか(ルール)はたくさんあり、どれを適用するかは選択できる
  • 注意してくれることが本業だけど、実行時に --fix オプションをつけると一部のルールにおいてはコードを修正してくれる
  • 自動でfixできるものはルール一覧の中でレンチマーク🔧がついているものに限られる

VSCodeのフォーマッタとは?

そもそもPrettier等のプラグインをインストールしなくてもTypeScriptのソースコードはフォーマットできるが、あれはどういうことか?という話。結論としては、VSCodeチームが作っているデフォルトのTSフォーマッタがもともとインストールされていて、それが動作しているだけ。

試しに、command + shift + pでOpen Default Settings(JSON)からdefaultSettings.jsonを開いてみると、"editor.defaultFormatter"のコメントでいくつか使えるものが書いてある。TSであればデフォルトでvscode.typescript-language-featuresのフォーマッタが動作するようだ

VSCodeのOrganize Imports機能とは?

TSで開発していると、一番上の行にimportをたくさん書くと思う。このimport句をアルファベット順にしたり、使っていないimportされた変数を消したり、途中で改行されていたら1行に整形したりする機能。非常に便利で、特に使っていないimport変数を勝手に消してくれるのはPrettierでもLintでも無理。変数を消したりすることを思えば、フォーマッタよりもLinterに近いイメージか。

TSLintとは?

  • ESLintと同じようなもの
  • なんだけど、あんまり活発じゃなくてTypeScript本家もTSLint使ってたけどESLintに変えていくらしいし、TSLint自体もESLintのプラグインにしていくらしく、今からは選択する理由はなさそう。
    参考:TypeScript on ESLintの未来
  • と思ってたらTSLintを使う理由を見つけた。2019年8月現在、AngularではデフォルトはTSLintのようで、ng newコマンドではAngularプロジェクトに最適化されたtslint.jsonが生成される。そのため、ESLintを使うにはこのtslint.jsonと同じような内容のものを自分で作るか見つけてくることになるのだが、結構たいへんなのでAngular側の公式対応がされるまではTSLintでよさそう。

それぞれの役割被らないの?

被ります。

まず、PrettierとVSCodeのデフォルトフォーマッタはがっつり被る。単純にPrettierがデフォルトフォーマッタの上位互換と言ってもいい。なので、もしVSCodeでPrettierプラグインをインストールした場合、フォーマッタにPrettierとデフォルトのどちらを使うかを選ぶことになる。

次に、ESLint(TSLint)とPrettierが被るかどうか。これが、一部ルールが被ってしまう。場合によってはPrettierが修正した内容だとESLint側でerrorになることもある。しかし、ESLintでしか出来ない文法のチェックや、Prettierにしかできない整形もやはり存在する。

そして、Organize Imports機能とPrettierも一部被ってしまう。例えば、1行が非常に長いimport句があった場合、

  • Prettier ⇢ 改行して整える
  • Organize Imports ⇢ 1行にする

という違いが出てしまう。

結局どういう環境を作ったか?

理想の環境

まず理想的な、自分が目指した姿は以下。

  • TSLintに引っかかったところは常にコード上でハイライト表示される(波線など)
  • ファイル変更後、保存時に自動的にPrettierで整形される
  • ファイル変更後、保存時に自動的にTSLintのfix可能なものはfixされる
  • ファイル変更後、保存時に自動的にOrganize Importsが走る

lintやprettierはそれだけでも単体のツールなので、例えば

$ eslint --fix app.ts
$ prettier app.ts

とかやればlintの警告一覧が出力されたり、自動で可能なものはfixされたり、prettierの整形が施されたりする。でも開発中にそんなコマンド打つのは面倒だから、VSCodeで保存した瞬間に自動で修正を走らせたい。修正できないものはできるだけ早く教えてほしいので下線とか出してほしい。ということで、これを目指して環境を作った。

現実の環境

いくつか事情があって、こうなった。

  • TSLintに引っかかったところは常にコード上でハイライト表示される(波線など)🎉🎉🎉
  • ファイル変更後、保存時に自動的にPrettierで整形される🎉🎉🎉
  • ファイル変更後、保存時に自動的にTSLintのfix可能なものはfixされる🎉🎉🎉
  • ファイル変更後、保存時に自動的にOrganize Importsが走らない😭😭😭

惜しかった… Organize Importsとの兼ね合いが難しかった。。。保存時にOrganize Importsを走らせるには、.vscode/settings.jsonに

"editor.codeActionsOnSave": {
    "source.organizeImports": true
}

と書けば良いだけなんだけど、これで実際動かすと、非常に長いimport文があったとき、Prettierとルールがバッティングして常にエラーが出てしまうということがあった。
Prettierが、長い行を整形する機能をoffにすれば共存可能だけど、そこは重要なので外したくなかった。

環境構築

手順

  1. VSCodeのマーケットプレイスからTSLint(ms-vscode.vscode-typescript-tslint-plugin)をインストール
  2. npm i -D prettier tslint-config-prettier tslint-plugin-prettier
  3. もとから用意されたtslint.jsonのrulesDirectory、rulesにprettier系の項目を追加
  4. もとから用意されたtslint.jsonから整形ルールを適宜削除
  5. .vscode/settings.json に以下を記述
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
    "source.organizeImports": false,
    "source.fixAll.tslint": true
}

説明

  • save時にTSLintでfixしたいのでTSLintはnpm installではなくVSCodeプラグインをinstall
  • prettierを使うためインストール (npm i -D prettier)
  • tslint-config-prettierは、tslintのルールのうち、prettierと被るルールを無効にするプラグイン
  • tslint-plugin-prettierはtslint実行時にprettierも同時実行してくれるプラグイン
  • rulesDirectoryやrulesに関してはこのあたりを参考に – TSLintを使うTypeScriptプロジェクトにコードフォオーマッタPrettierを導入する

ちなみにこのQiitaの記事でもあるけど、自分もprettierのsingleQuoteはtrueにしたし、あとTSLintの"no-inferrable-types"がもともとtrueだったけど消して、それとは逆にtypedefをtrueにして型指定をしっかり書かないといけないルールにしました。

いやー疲れた。もうちょっとLintとPrettierとOrganize Imports、仲良くしてほしい!!!!!😂