テックタッチアドベントカレンダー23日目を担当するフロントエンドエンジニアの ozaan です。
最近ハマっているものは、カルディなどで売られているホーマーロイヤルミルクティベース無糖です。よろしくお願いします。
弊社では、フロントエンド領域の技術スタックとして TypeScript, React, Nx, etc. を採用しており、JavaScript の Linter としてデファクトスタンダード(2022年現在)である ESLint も導入しています。
当記事では、ESLint が TypeScript 固有の構文などを解析可能になるプラグインである typescript-eslint の Supported Rules に着目し、社内リポジトリでまだ適用していないルールのうち一定の効果が見込めるものを紹介します。
今回の方針
Linter のルールを追加することによって、以下の効果が得られます。
- 既存のコードの形式に対して警告を出す、または自動で訂正を行なう
- 今後追加されるコードの形式を一定に保つ
前者はリポジトリに適用することで即座に効果が確認できる一方で、後者は既存のコード構成が合意された経緯などに対する広い視野での理解が必要であり、入社3か月程度の自分の視野では後者に基づいた判断は難しいと考えました。
そのため今回は、前者の「既存のコードの形式に対して警告を出す、または自動で訂正を行なう」という点を重視し、警告の数や自動訂正によるコードの差分が比較的多いルールを調べることにしました。
ルールの探し方
ESLint の CLI を使って現在適用中のルール名を列挙したものと、公式リファレンスの Supported Rules のページからルール名のリストを抽出してそれらを突き合わせて… というような手順で進めたかったのですが、
社内リポジトリで利用している typescript-eslint のバージョンと公式リファレンスのそれが一致していなかった
=> 一部のルール名が変更されている可能性があった
=> 未適用のルール名を洗い出すことが困難
という理由から、ルールの詳細ページを1つずつ開きながら適用できそうなものを泥臭く確認していく形を取りました。
弊社のフロントエンド用リポジトリの Linting には、Nx が提供している @nrwl/eslint-plugin-nx プラグインに含まれている @nrwl/nx/typescript プラグイン、および .eslintrc.json ファイルでルール名を直接指定する形で適用しているため、それらは今回の検討からは外しました。
以下は、ルール名を直接指定して適用している typescript-eslint ルールの紹介になります。
現在適用中のルール
@typescript-eslint/no-non-null-assertion
非 null アサーション演算子 !
の利用を許可しません。
null
または undefined
が返却される可能性のある式を取り除き、null 安全を実現します。
@typescript-eslint/consistent-type-assertions
as
句を使った型アサーションを許可しません。(any
型や unknown
型へのアサーションは無視されます)
@typescript-eslint/interface-name-prefix
interface の命名で先頭に I
をつけるよう強制します。
(2022年12月現在の最新バージョンである v5.46.1 では、 naming-convention ルールにオプションを加えることで再現可能です)
@typescript-eslint/no-unused-vars
var
句を使った変数宣言を許可しません。(ESLint にある同名のルールの拡張版)
@typescript-eslint/ban-types
String
オブジェクトや Boolean
オブジェクトなどの、JavaScript で定義されているプリミティブ値のラッパーオブジェクトを型定義に利用することを許可しません。
@typescript-eslint/no-extra-semi
セミコロンの記述を必要最低限(例:即時関数の定義 ;(() => {})()
)のもの以外許可しません。(ESLint にある同名のルールの拡張版)
@typescript-eslint/prefer-ts-expect-error
型検査を回避するための @ts-ignore
ディレクティブの利用を許可しません。(その上位互換である @ts-expect-error
ディレクティブは無視されます)
@typescript-eslint/no-explicit-any
明示的な any
型の利用を許可しません。
(@nrwl/nx/typescript プラグイン自体にはルールの適用は無く(厳密には4つのルールが無効化されています)、そこに含まれている @typescript-eslint/recommended プラグインによってルールが適用されています)
ルールの紹介
まだ適用していない & 一定の効果が見込める & 導入が楽そうなルール
@typescript-eslint/prefer-optional-chain
連鎖したプロパティの存在判定式にオプショナルチェーン ?.
の利用を強制します。
@typescript-eslint/prefer-includes
文字列や配列の中に指定する値が存在しているかを判定する式として array.indexOf(value) !== -1
の代わりに array.includes(value)
の記述を強制します。
まだ適用していない & 一定の効果が見込める & 導入までの道のりが険しいルール
@typescript-eslint/no-unsafe-member-access
any
型のオブジェクトのメンバー変数への参照を許可しません。
外部ライブラリやDOM周りの型定義が追いついていない部分もあるため、網羅するにはかなり苦労しそうです。
@typescript-eslint/restrict-template-expressions
テンプレートリテラル ${arg}
を使った暗黙的な型変換を許可しません。
実際に適用する場合は、allowNumber などのオプションを指定して実装しやすさと安全性のバランスを取るのがベターであると見ています。
上記計4つは、 公式リファレンスにて strict や recommended として指定されているルールから、実際に手元で適用してみてエラーや差分が多く出たものから挙げました。
ルールを追加 & 修正を施した Pull Request は、影響範囲が広いためリリース作業などが落ち着き次第提出するつもりです。
道中で遭遇した問題とその解決など
- CLI 実行時に以下のエラーが表示される
Error: You have attempted to use a lint rule which requires the full TypeScript type-checker to be available, but you do not have `parserOptions.project` configured to point at your project tsconfig.json files in the relevant TypeScript file "overrides" block of your project ESLint config `apps/broker/.eslintrc.json`
.eslintrc.json の最上位のプロパティとして、もしくは
overrides
プロパティの1つの要素として以下のプロパティを追加する必要がある"parserOptions": { "project": ["./tsconfig.base.json"] },
CLI 実行時に以下のようなエラーが表示される
Definition for rule '@typescript-eslint/<ルール名>' was not found
- 最新のバージョンではルール名が変わっていることがある
- リファレンスにはルール名の変遷について記載されていない...
- リファレンスのリポジトリ の当該のルールの .md ファイルの差分を追う必要がある
- CLI 実行時の
--fix
オプションが効かない- https://typescript-eslint.io/rules/ で 🔧 がついてるルールのみ自動修正可能(執筆当初はこの仕様に気づきませんでした;;)
ふりかえり
個人的に ESLint の導入(または Lint ルールの適用)にはあまり良い思い出がなかったのですが、今回の調査を通じて以下のような学びを得られました。
- typescript-eslint ルールの Example から TypeScript におけるバッドプラクティス
- ESLint プラグインの実体 (GitHub 上にあるコード) の追い方
- ルールのうち、どれが推奨されているものなのか・自動修正が可能なものなのかといったことを把握できた & 継続的に把握できるようになった
- typescript-eslint 公式リファレンスの Overview から確認できます
typescript-eslint だけでなく本家の ESLint のルールも追いながら、今後も継続的に社内リポジトリに貢献していきたいです。
最後まで読んでいただきありがとうございました。