chilitreat blog

開発,プログラミング,趣味系をまとめます.Qiitaと差別化したい

typescript-eslintの導入方法とディレクトリごとにルールを設定する方法

メモがてから自分用に使い方をまとめて見る。 少し前まではTypeScript本体やESLintと@typescript-eslintのバージョンを揃えないと一気に設定が壊れる印象だったが最近は安定してそう

検証したバージョン

  • npm v6.13.4
  • Node.js v10.19.0
  • typescript v4.3.5
  • eslint v7.32.0
  • @typescript-eslint/eslint-plugin v4.29.2
  • @typescript-eslint/parser v4.29.2

typescript-eslintはがサポートしているTypeScriptのバージョンは >=3.3.1 <4.6.0(2022/01/09現在)

導入方法

基本的に公式のGet Startedに書いてある通り

Linting your TypeScript Codebase | TypeScript ESLint

インストール

npm install --save-dev eslint typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin

.eslintrc.js の設定

プロジェクトルートに、.eslintrc.js という名前でファイルを作成。以下をコピー&ペーストする

package.jsonに設定を書くこともできるが、eslintrc.jsに書く方がゴチャらないので好み

module.exports = {
  root: true,
  parser: '@typescript-eslint/parser',
  plugins: [
    '@typescript-eslint',
  ],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
  ],
};

ちなみに、eslintrc.js以外にも様々なファイル名(拡張子)が使用可能
JSONでもYAMLでもコメントが書けるのでどれを選ぶかはお好みで

  • .eslintrc.js
  • .eslintrc.cjs
  • .eslintrc.yaml
  • .eslintrc.yml
  • .eslintrc.json
  • ( package.json )

eslint.org

eslintrcの各設定項目の意味を解説する

root: true

ESLint実行時、解析されるファイルと同じディレクトリ階層にeslintrcが存在した場合は、そのeslintrcに記載された設定が優先される(後勝ちの設定

root: true と記載されたeslintrcが見つかるまで子ディレクトリから親ディレクトリへ走査する。途中で見つからない場合は ルートディレクトリまで走査する

プロジェクトルートのeslintrcにroot: trueと記載すると、親ディレクトリ以上は走査しないため意図しない設定のマージが防げる

eslintrcとpackage.jsonが同じ階層にあった場合は、eslintrcが優先され、package.jsonは無効化される

parser:

ESLintがTypeScriptの構文を解釈できるように@typescript-eslint/parserを指定する

plugins:

インストールしたプラグインを読み込ませるために@typescript-eslintを指定する

extends:

予め用意された設定を拡張として読み込む eslint:recommended, plugin:@typescript-eslint/recommended, の二つを記載することで、いい感じに設定された推奨ルールを読み込んでいる

extends の設定方法に色々なパターンがあるが、この記事がとても分かりやすかった

zenn.dev

■ 他に設定できること

rules, ignorePatternsなどが設定可能

詳細はこちらを参照 eslint.org

基本的な設定項目

eslint:recommended で設定されているルール

このページの一番左列に✔︎マークがついている項目

eslint.org

plugin:@typescript-eslint/recommended で設定されているルール

このページの✅列に✅マークがついている項目

typescript-eslint.io

plugin:@typescript-eslint/eslint-recommended

plugin:@typescript-eslint/recommended の中で extendsされている

https://github.com/typescript-eslint/typescript-eslint/blob/97c0e8606705dc0b4ef8b4ee5206111276c8a170/packages/eslint-plugin/src/configs/recommended.ts#L6

この記事でも言及されているように、plugin:@typescript-eslint/recommendedが指定されている場合は、追加でextendsさせる必要は無い

zenn.dev

ディレクトリごとにルールを設定する

TypeScriptで書かれたバックエンドアプリケーションを想定

基本的にキャメルケースで統一しつつも、データベースのインタフェースのプロパティ名はスネークケースで定義するeslintrcの設定をする

- `app/**/*.ts` のinterfaceの属性名をキャメルケースに設定する
- `app/infrastructure/database/*.ts` のinterfaceのプロパティ名をスネークケースに設定する

命名規則を設定するには、@typescript-eslint/naming-convention を適用する

typescript-eslint/naming-convention.md at main · typescript-eslint/typescript-eslint · GitHub

しかし@typescript-eslint/naming-convention のルールだけでは、特定のディレクトリ配下のファイルにだけ別のルールを適用することができない

github.com

そのため、

  • orverridesを指定してルールを上書きするか
  • 別のルールを適用したいファイルが存在するディレクトリに、.eslintrcを配置するか のどちらかで解決する必要がある。

今回は前者のorverridesを指定してルールを上書きする方で解決する

先ほど作成した eslintrc.jsに以下を追記する

...
rules: {
  "@typescript-eslint/naming-convention": [
    "error",
    { 'selector': 'property', 'format': [ 'camelCase' ] }    // Group Selectorsの property。classProperty, objectLiteralProperty, typePropertyをキャメルケースで指定する
  ]
},
overrides: [
  {
    "files": [ "app/infrastructure/database/*.ts" ], // 上書き対象のGlobパターン
    "rules": {
      "@typescript-eslint/naming-convention": [
        "error",
        { 'selector': 'property', 'format': ['snake_case'] }
      ]
    }
  }
]
...

全てファイルに { 'selector': 'property', 'format': [ 'camelCase' ] } のルールが適用されて困る場合は、orverridesに解析を無視するGlobパターンを記載すれば良い

...
rules: {
  "@typescript-eslint/naming-convention": [
    "error",
    { 'selector': 'property', 'format': [ 'camelCase' ] }    // classProperty, objectLiteralProperty, typePropertyをキャメルケースで指定する(Group Selectors)
  ]
},
overrides: [
  {
    "files": [ "app/infrastructure/database/*.ts" ],
    "rules": {
      "@typescript-eslint/naming-convention": [
        "error",
        { 'selector': 'property', 'format': ['snake_case'] }
      ]
    }
  },
  // ここ追加
  {
    "files": [ "app/infrastructure/api/*.ts" ], // apiは外部APIの仕様によって様々なので無視する
    "rules": {
      "@typescript-eslint/naming-convention": [
        "off",
        { 'selector': 'property', 'format': [ 'camelCase' ] } // rulesに書いたルールを指定する
      ]
     }
  }
]
...

このeslintrcを設置することで、ディレクトリごとに異なるルールが設定可能になった

すでに開発が進んでいるプロジェクトで導入する際は、全ファイルで共通したルールを設定した後、ディレクトリごとにルールを無効化するルールをorveridesに追加し、徐々に無効化したルールを有効化してくと比較的辛くなく導入できるはず

コードレビューで好みをの話をされるとモヤるのでLintを整備して秩序と安寧を作っていきたい

参考情報

  1. Configuration Files - ESLint - Pluggable JavaScript linter
  2. ESLint 設定を作成する技術
  3. ESLintでTypeScriptの変数の名付け規則をチェックしよう! | DevelopersIO
  4. うわっ...私の.eslintrc、無駄が多すぎ...?
  5. [naming-convention] Cannot disable individual rule options · Issue #2755 · typescript-eslint/typescript-eslint · GitHub