🔯

Angular ESLintの導入と推しルール6選(2023)

2023/12/17に公開

はじめに

今回は、Angular ESLintの導入と推しルール6選について紹介します。

Angular ESLintとは

Angular ESLintは、ESLintでAngularプロジェクトをlintするための便利なルールがまとまっているライブラリです。
https://github.com/angular-eslint/angular-eslint

Angular ESLintができた経緯

ESLintは2023年現在、JavaScriptのリンター、静的解析ツールとして広く使われていますが、もともとAngular(not AngularJS)では、TypeScriptが導入されていたこともあり、当時使われていたTypeScriptのリンターであるTSLintが使用されていました。しかし、TSLintが2020年にアーカイブされ非推奨になり、Angularでは、バージョン12でTSLintとAngular用のTSLintルールセットライブラリであったCodelyzerが非推奨になりました。それから、TSLintをESLintのプラグインとして統合していくtypescript-eslintと、それを使ってCodelyzerを再現しつつ、新たなルールセットライブラリとしてのAngular ESLintができました。

そのため、基本的にはCodelyzerが非推奨になったAngularバージョン12以降からのサポートが充実しています。この記事では、最新のバージョン17で検証しています。

Angular ESLintを導入する

導入方法

ng newなどで作成済みのAngularアプリケーションに、次のコマンドで導入することができます。

ng add @angular-eslint/schematics

https://github.com/angular-eslint/angular-eslint?tab=readme-ov-file#quick-start

変更される箇所

angular.jsonにlintのコマンド、cliの設定が記述され、package.jsonにnpm run lintng lintが実行できるように設定が記述されます。

また、eslintの設定ファイルがない場合は.eslintrc.jsonが生成され、ある場合は次のような設定が追記されます。

.eslintrc.json
{
  "root": true,
  "ignorePatterns": [
    "projects/**/*"
  ],
  "overrides": [
    {
      "files": [
        "*.ts"
      ],
      "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:@angular-eslint/recommended",
        "plugin:@angular-eslint/template/process-inline-templates"
      ],
      "rules": {
        "@angular-eslint/directive-selector": [
          "error",
          {
            "type": "attribute",
            "prefix": "app",
            "style": "camelCase"
          }
        ],
        "@angular-eslint/component-selector": [
          "error",
          {
            "type": "element",
            "prefix": "app",
            "style": "kebab-case"
          }
        ]
      }
    },
    {
      "files": [
        "*.html"
      ],
      "extends": [
        "plugin:@angular-eslint/template/recommended",
        "plugin:@angular-eslint/template/accessibility"
      ],
      "rules": {}
    }
  ]
}

注目する点はextendsに記述される@angular-eslintのpluginです。

@angular-eslint/recommended

これは、tsファイルに記述されているTypeScriptに対して、Angular ESLintが推奨するルールを有効にするプラグインです。
推奨ルールの一覧は、packages/eslint-plugin/src/configs/recommended.jsonにあります。

すべての各ルールについての詳細な一覧は、packages/eslint-pluginのREADMEにまとめられており、スタイルガイドへのリンクが付いているものもあるので読んでみると学びがありました。

ちなみに、@angular-eslint/recommended@angular-eslint/allにするとすべてのルールが有効になります。

@angular-eslint/template/process-inline-templates

これは、tsファイルに記述されているコンポーネントなどのインラインテンプレートのhtmlに対して、@angular-eslint/templateのルールを適応するためのプラグインです。

@angular-eslint/template/recommended

これは、htmlファイルに記述されているテンプレートのhtmlに対して、Angular ESLintが推奨するルールを有効にするプラグインです。
推奨ルールの一覧は、packages/eslint-plugin/src/configs/recommended.jsonにあります。

すべての各ルールについての詳細な一覧は、packages/eslint-plugin-templateのREADMEにまとめられています。

ちなみにこちらも、@angular-eslint/template/recommended@angular-eslint/template/allにすると、すべてのルールが有効になります。

@angular-eslint/template/accessibility

こちらは、htmlファイルに記述されているテンプレートのhtmlに対して、Angular ESLintにあるアクセシビリティ系のルールを適応するためのプラグインです。

アクセシビリティ系のルールの一覧は、packages/eslint-plugin-template/src/configs/accessibility.jsonにあります。

余談:Prettierの導入について

余談ですが、eslintを一緒に使うことの多いコードフォーマッターPrettierを一緒に導入するためのeslint-plugin-prettierの設定についてREADMEに書いてあったのが親切だと思いました。(PRした人えらい)

ちなみにeslint-plugin-prettier推奨構成を参考にすると、次のように導入するのが良さそうです。

npm install -D eslint-plugin-prettier eslint-config-prettier
.eslintrc.json
...
      "files": [
        "*.ts"
      ],
      "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:@angular-eslint/recommended",
        "plugin:@angular-eslint/template/process-inline-templates"
+       "plugin:prettier/recommended",
      ],
...
      "files": [
        "*.html"
      ],
      "extends": [
        "plugin:@angular-eslint/template/recommended",
        "plugin:@angular-eslint/template/accessibility",
+       "plugin:prettier/recommended",
      ],

自分の環境ではsrc/index.htmlの!doctype宣言でprettierがparse errorになったので、一旦.eslintignoreに追加しました。

.eslintignore
src/index.html

個人的な推しルール6選

@angular-eslint/no-output-native

ドキュメントのリンク
これは、ts側の@Outputで使うイベントに標準のDOMイベントと同じ名前をつけないようにするルールです。
@angular-eslint/recommendedで有効になります。

これを有効にすることで標準のDOMイベントと@Outputによるイベントで、2度イベントが発火している箇所に気づけたというメリットがありました。対応している標準のDOMイベントの一覧は、get-native-event-names.tsにあります。

次のようなコードがエラーになります。

sample.component.ts
@Component(...)
class SampleComponent {
   @Output() change: new EventEmitter<string>();
             ~~~~~~
}

こう直すと、エラーになりません。

sample.component.ts
@Component(...)
class SampleComponent {
-  @Output() change: new EventEmitter<string>();
+  @Output() changeText = new EventEmitter<string>();
}

@angular-eslint/prefer-on-push-component-change-detection

ドキュメントのリンク
これは、ts側のコンポーネントのchangeDetectionChangeDetectionStrategy.OnPushに設定されていることを確認するルールです。
@angular-eslint/recommendedでは有効になりません。

ChangeDetectionStrategy.OnPushはコンポーネントに設定することで、必要のない変更検知を減らすことのできるオプションです。Zonelessになるのはもう少しかかりそうなので、これを静的解析で気付き、warnに設定して段階的に直していけるのは良いなと思って選出しました。

次のようなコードがエラーになります。

sample.component.ts
@Component({
...
~~~~~~~~~~~~~
})
class SampleComponent {
}

こう直すと、エラーになりません。

sample.component.ts
@Component({
...
+  changeDetection: ChangeDetectionStrategy.OnPush,
})
class SampleComponent {
}

@angular-eslint/use-component-view-encapsulation

ドキュメントのリンク
これは、ts側のコンポーネントのViewEncapsulation.Noneの使用を禁止するルールです。
@angular-eslint/recommendedでは有効になりません。

ViewEncapsulation.Noneはコンポーネントに設定することで、ビューのスタイルのカプセル化をやめ、スタイルをグローバルに適用するオプションです。自分がこのオプションが設定されているのに出会ったことは数回しかないですが、既存の大規模なAngularプロジェクトだと設定されていることがあるのかなと想像しています。そのためこれを静的解析で気付き、warnに設定して段階的に直していけるのは良いなと思って選出しました。

次のようなコードがエラーになります。

sample.component.ts
@Component({
  ...
  encapsulation: ViewEncapsulation.None,
                                   ~~~~
})
class SampleComponent {
}

こう直すと、エラーになりません。

sample.component.ts
@Component({
- encapsulation: ViewEncapsulation.None,
})
class SampleComponent {
}

@angular-eslint/prefer-standalone-component

ドキュメントのリンク
これは、ts側のコンポーネントのstandaloneプロパティがtrueに設定されていることを確認するルールです。
@angular-eslint/recommendedでは有効になりません。

standaloneプロパティはコンポーネントに設定することで、@NgModuleを介さずともコンポーネントを解釈できるようにする機能です。Standalone Componentsマイグレーションするコマンドが提供されているのですが、これも諸般の事情などで一気にマイグレーションできない時に、warnに設定して意識して段階的に直していけるのは良いなと思って選出しました。ちなみにこのルールはコンポーネントに限られているため、DirectiveやPipeなどのルールが欲しいと思ったらコントリビュートチャンスかもしれないです。

次のようなコードがエラーになります。

sample.component.ts
@Component({
...
~~~~~~~~~~~~~
})
class SampleComponent {
}

こう直すと、エラーになりません。

sample.component.ts
@Component({
...
+  standalone: true,
})
class SampleComponent {
}

@angular-eslint/template/prefer-ngsrc

ドキュメントのリンク
これは、html側のテンプレートで使われているimg要素のsrc属性の代わりにngSrcが使用されるようにするルールです。
@angular-eslint/template/recommendedでは有効になりません。

ngSrcはimg要素に設定することで、NgOptimizedImageを機能を使って画像を最適化できます。これもwarnに設定して意識して段階的に直していけるのは良いなと思って選出しました。NgOptimizedImageに関しては、Angular Advent Calendar 2023の14日目に@hoshimaさんがNgOptimizedImageに入門するという記事も書かれています。

次のようなコードがエラーになります。

sample.component.html
   <img [src]="value">
       ~~~~~~~~~~~~~

こう直すと、エラーになりません。

sample.component.html
-  <img [src]="value">
+  <img [ngSrc]="value">

@angular-eslint/template/prefer-control-flow

ドキュメントのリンク
これは2023年12月現在、開発者プレビューであるBuilt-in control flowをhtml側のテンプレートで使うようにするルールです。
@angular-eslint/template/recommendedでは有効になりません。

Built-in control flowはAngularテンプレートの新しい構文で、@if@for@deferブロックなどが使えるようになります。マイグレーションするコマンドも提供されているようですが、これも諸般の事情などで一気にマイグレーションできない場合にwarnに設定して段階的に直していけるのは良いなと思って選出しました。
Built-in control flowに関しては、Angular Advent Calendar 2023の13日目に@noxi515さんがControl FlowとViewレンダリングという記事も書かれています。

次のようなコードがエラーになります。

sample.component.html
   <div *ngIf="condition; else elseBlock">true</div>
        ~~~~~~~~~~~~~~~~~
   <ng-template #elseBlock>
      <div>false</div>
   </ng-template>

こう直すと、エラーになりません。

sample.component.html
-  <div *ngIf="condition; else elseBlock">true</div>
-  <ng-template #elseBlock>
-     <div>false</div>
-  </ng-template>
+  @if (condition) {
+   <div>true</div>
+ } @else {
+   <div>false</div>
+ }

おわりに

Angular ESLintについて、リポジトリを見つつルールをまとめてみました。
以前見た時よりもREADMEの量も更新され充実していて、ルール一覧のドキュメントに貼ってあるスタイルガイドのリンクやルールのドキュメントなど学びになることが多かったです。
また今回は紹介しなかったですが、アクセシビリティ周りのルールが多いのが知らなくて、今度使ってみようと思いました。
個人的にはWebフロントエンド開発者にとってLinterは、LSP(Language Server Protocol)やテストなどのように開発者体験やコーディングの効率化にかなり関わってくるものだと考えています。Angular ESLintを使ってAngularの機能が追加・変更された時に便利になる、できる可能性があるので、今後も見ていきたいです。

ここまで記事をお読みいただきありがとうございました!

Voicyテックブログ

Discussion