Angular 22 の新しいプロジェクトには lint(コードの書き方の検査)の設定が入っていません。ng add angular-eslint を1回実行すると、eslint.config.js(ESLint の flat config 形式)と angular.json の lint ターゲットが作られ、ng lint が使えるようになります。

ng add angular-eslint     # 設定ファイルの作成とパッケージの追加
ng lint                   # 検査(問題があると終了コード 1)
ng lint --fix             # 自動で直せるものを直す
ng add で起きること 中身
作られるファイル eslint.config.js(1004 バイト)
angular.json lint ターゲット(@angular-eslint/builder:lint)と schematicCollections
package.json "lint": "ng lint" と、devDependencies に4つ(angular-eslint・eslint・@eslint/js・typescript-eslint)
既定で有効な Angular 用ルール .ts に15個(推奨13+セレクタ名2)、.html に15個(推奨4+アクセシビリティ11)

確認環境:Angular CLI 22.2.2 で ng new したプロジェクトに ng add angular-eslint を実行しました(angular-eslint 22.6.0、ESLint 10.12.0、typescript-eslint 8.71、TypeScript 6.0.3、Node.js 24.21.0、Linux)。生成ファイル・差分・指摘の文・終了コード・ルールの数は、すべて実際に実行して得たものです。GitHub Actions の YAML は、同じコマンドを並べた例です。ルールの一覧は、インストールした @angular-eslint/eslint-plugin と eslint-plugin-template の各ルールの meta 情報から出しました。仕様は angular.dev の ng lint のページと angular-eslint の GitHub リポジトリの README で確かめました(確認日 2026年10月13日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「angular-eslintの設定|ng lintの導入とルール一覧」

導入:ng lint を最初に打つと

ng new した直後に ng lint を実行すると、次のメッセージで止まりました。

Cannot find "lint" target for the specified project.
You can add a package that implements these capabilities.

For example:
  ESLint: ng add angular-eslint

angular.dev の ng lint のページにも、「ng lint を使うには、lint の機能を持つパッケージを ng add で追加する」と書かれています。メッセージのとおり ng add angular-eslint を実行します。

ng add angular-eslint --skip-confirmation
✔ Searching for compatible package version
› Found compatible package version: 22.6.0.
    angular-eslint v22 matches this workspace's Angular v22.
    All angular-eslint dependencies have been successfully installed 🎉
    We detected that you have a single project in your workspace and no existing linter wired up, so we are configuring ESLint for you automatically.
CREATE eslint.config.js (1004 bytes)
UPDATE package.json (924 bytes)
UPDATE angular.json (2311 bytes)
✔ Packages installed successfully.

Angular 22 のプロジェクトには、angular-eslint の 22 系が選ばれました。angular-eslint の README では、@angular-eslint/ のメジャー版は Angular のメジャー版に合わせる(v12 以降)、とされています。angular-eslint は、ESLint のプラグイン・テンプレート用パーサー・ng lint のビルダー・スキーマ(@angular-eslint/schematics)をまとめたパッケージで、npm ls で見ると、これらが一緒に入っていました。

--skip-confirmation は「パッケージを入れてよいか」の確認を飛ばすオプションです。付けなければ、対話で確認が出ます。

angular.json と package.json の差分

   "cli": {
-    "packageManager": "npm"
+    "packageManager": "npm",
+    "schematicCollections": ["angular-eslint"]
   },
 ...
         "test": {
           "builder": "@angular/build:unit-test"
+        },
+        "lint": {
+          "builder": "@angular-eslint/builder:lint",
+          "options": {
+            "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
+          }
         }
-    "test": "ng test"
+    "test": "ng test",
+    "lint": "ng lint"
 ...
+    "@eslint/js": "^10.0.1",
+    "angular-eslint": "22.6.0",
+    "eslint": "^10.12.0",
+    "typescript-eslint": "8.71.0",

schematicCollections に angular-eslint が入ったので、以後の ng generate では angular-eslint のスキーマ(ひな形)も候補になります。

生成された eslint.config.js(flat config)

// @ts-check
const eslint = require('@eslint/js');
const { defineConfig } = require('eslint/config');
const tseslint = require('typescript-eslint');
const angular = require('angular-eslint');

module.exports = defineConfig([
  {
    files: ['**/*.ts'],
    extends: [
      eslint.configs.recommended,
      tseslint.configs.recommended,
      tseslint.configs.stylistic,
      angular.configs.tsRecommended,
    ],
    processor: angular.processInlineTemplates,
    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: [angular.configs.templateRecommended, angular.configs.templateAccessibility],
    rules: {},
  },
]);
部分 意味
files: ['**/*.ts'] のブロック TypeScript のファイルに当てる設定
eslint.configs.recommended ESLint 本体の推奨ルール
tseslint.configs.recommended・stylistic typescript-eslint の推奨ルールと、書き方をそろえるルール
angular.configs.tsRecommended Angular 用の推奨ルール(13個)
processor: angular.processInlineTemplates .ts の中の template: \…`` を取り出して、HTML のルールでも調べる
rules の2つ セレクタ名の決まり(コンポーネントは app-xxx、ディレクティブは appXxx)
files: ['**/*.html'] のブロック テンプレートに当てる設定。推奨(4個)とアクセシビリティ(11個)

angular-eslint の README には、flat config だけに対応していて、以前の .eslintrc 形式には対応しない、と書かれています。.eslintrc.json で書かれた記事の設定は、そのままでは使えません。flat config の書き方全般は、関連記事「ESLint flat config設定|v10対応の完成形コピペ集」で扱っています。

eslint --print-config で、実際に有効になったルールを数えた結果です。

ファイル 有効なルール 内訳
.ts 97 ESLint 本体 49・typescript-eslint 33・angular-eslint 15
.html 15 テンプレート推奨 4・アクセシビリティ 11

既定で有効な Angular 用ルール(28個)と意味

TypeScript 側(tsRecommended:13個)

ルール(@angular-eslint/ を省略) 既定 何を指摘するか
contextual-lifecycle error そのクラスの種類では呼ばれないライフサイクル(Pipe の ngOnInit など)
no-empty-lifecycle-method error 中身が空の ngOnInit() など
no-input-rename error @Input('別名') のような入力の別名
no-inputs-metadata-property error @Component({ inputs: [...] }) の書き方
no-output-native error click など DOM のイベント名と同じ出力名
no-output-on-prefix error onSave のような on で始まる出力名
no-output-rename error @Output('別名') のような出力の別名
no-outputs-metadata-property error @Component({ outputs: [...] }) の書き方
prefer-inject error コンストラクタの引数での DI(inject() を勧める)
prefer-on-push-component-change-detection error 既定の OnPush をやめて Eager/Default にしたコンポーネント
prefer-standalone error standalone: false
use-pipe-transform-interface error PipeTransform を implements していない Pipe
use-lifecycle-interface warn ngOnInit があるのに implements OnInit が無い

prefer-on-push-component-change-detection の文は「既定の ChangeDetectionStrategy.OnPush をやめないこと」です。@angular/core 22.2.2 の型定義では OnPush に「NOTE: OnPush is enabled by default.」とあり、Default は @deprecated Use Eager instead. になっています。v22 では、何も書かないコンポーネントは OnPush です。OnPush のしくみは、関連記事「Angular変更検知とOnPush|markForCheckの使い方」で扱っています。

テンプレート側(templateRecommended:4個・templateAccessibility:11個)

ルール(@angular-eslint/template/ を省略) 種類 何を指摘するか
banana-in-box 推奨 ([value]) のような逆向きの双方向バインディング
eqeqeq 推奨 テンプレートの ==・!=
no-negated-async 推奨 !(obs$ | async) のような async パイプの否定
prefer-control-flow 推奨 *ngIf・*ngFor・[ngSwitch](@if などを勧める)
alt-text a11y alt の無い <img>
click-events-have-key-events a11y キーボードのイベントが無い (click)
elements-content a11y 中身の無い見出し・リンク・ボタン
interactive-supports-focus a11y (click) があるのにフォーカスできない要素
label-has-associated-control a11y 入力欄と結び付いていない <label>
mouse-events-have-key-events a11y mouseover・mouseout だけで focus・blur が無い
no-autofocus a11y autofocus 属性
no-distracting-elements a11y <marquee>・<blink>
role-has-required-aria a11y role に必要な aria-* が足りない
table-scope a11y <th> 以外の scope 属性
valid-aria a11y 存在しない aria-* 属性や、正しくない値

a11y はアクセシビリティ(障害のある人も含めて使えるか)のことです。

よく出る指摘と直し方(実際の出力)

わざと間違えたコンポーネントを ng lint にかけました。

// bad.ts(直す前)
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { NgIf, AsyncPipe } from '@angular/common';
import { Observable, of } from 'rxjs';

@Component({
  selector: 'my-bad',
  imports: [NgIf, AsyncPipe],
  templateUrl: './bad.html',
  changeDetection: ChangeDetectionStrategy.Default,
})
export class Bad implements OnDestroy {
  @Input('userName') name = '';
  @Output() onSave = new EventEmitter<void>();
  @Output() click = new EventEmitter<void>();
  count = 0;
  loading$: Observable<boolean> = of(false);
  data: any;

  constructor(private http: HttpClient) {}

  ngOnInit() {}

  ngOnDestroy() {
    console.log('destroy');
  }
}
<!-- bad.html(直す前) -->
<div *ngIf="count == 0">ゼロ</div>
<input ([value])="name">
<img src="logo.png">
<div (click)="onSave.emit()">保存</div>
@if (!(loading$ | async)) {
  <p>読み込み完了</p>
}
<label>名前</label><input type="text">
Linting "demo"...

src/app/bad/bad.html
  1:7   error  Use built-in control flow instead of directive ngIf  @angular-eslint/template/prefer-control-flow
  1:13  error  Expected `===` but received `==`  @angular-eslint/template/eqeqeq
  2:8   error  Invalid binding syntax. Use [(expr)] instead  @angular-eslint/template/banana-in-box
  3:1   error  <img/> element must have a text alternative  @angular-eslint/template/alt-text
  4:1   error  click must be accompanied by either keyup, keydown or keypress event for accessibility  @angular-eslint/template/click-events-have-key-events
  4:1   error  Elements with interaction handlers must be focusable  @angular-eslint/template/interactive-supports-focus
  5:6   error  Async pipe results should not be negated. Use `(observable | async) === false`, `(observable | async) === null`, or `(observable | async) === undefined` to check its value instead  @angular-eslint/template/no-negated-async
  8:1   error  A label component must be associated with a form element  @angular-eslint/template/label-has-associated-control

src/app/bad/bad.ts
   7:13  error    The selector should start with one of these prefixes: "app" (https://angular.dev/style-guide#choosing-component-selectors)  @angular-eslint/component-selector
  10:44  error    Components should not opt out of the default `ChangeDetectionStrategy.OnPush` change detection strategy  @angular-eslint/prefer-on-push-component-change-detection
  13:10  error    Input bindings should not be aliased (https://angular.dev/guide/components/inputs#choosing-input-names)  @angular-eslint/no-input-rename
  14:13  error    Output bindings, including aliases, should not be named "on", nor prefixed with it (https://angular.dev/guide/components/outputs#choosing-event-names)  @angular-eslint/no-output-on-prefix
  15:13  error    Output bindings, including aliases, should not be named as standard DOM events  @angular-eslint/no-output-native
  18:9   error    Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
  20:15  error    Prefer using the inject() function over constructor parameter injection. Use Angular's migration schematic to automatically refactor: ng generate @angular/core:inject  @angular-eslint/prefer-inject
  22:3   warning  Lifecycle interface 'OnInit' should be implemented for method 'ngOnInit'. (https://angular.dev/style-guide#use-lifecycle-hook-interfaces)  @angular-eslint/use-lifecycle-interface
  22:3   error    Lifecycle methods should not be empty  @angular-eslint/no-empty-lifecycle-method
  22:14  error    Unexpected empty method 'ngOnInit'  @typescript-eslint/no-empty-function

✖ 18 problems (17 errors, 1 warning)
  1 error and 1 warning potentially fixable with the `--fix` option.

(表示を見やすくするため、パスと空白を詰めています。)18件のうち、--fix で直せるのは2件でした。 console.log は、既定の設定では指摘されませんでした(no-console は ESLint の推奨に入っていません)。

指摘ごとの直し方

指摘(ルール) 直し方
prefer-control-flow *ngIf → @if (…) { }。ng generate @angular/core:control-flow で一括移行もできる
eqeqeq == → ===
banana-in-box ([x]) → [(x)](--fix で直る)
alt-text <img alt="…">。飾りの画像は alt=""
click-events-have-key-events・interactive-supports-focus <div (click)> をやめて <button type="button"> にする
no-negated-async @if ((loading$ | async) === false)
label-has-associated-control <label for="name"> と <input id="name">、または <label> で入力欄を囲む
component-selector selector: 'app-bad'
prefer-on-push-component-change-detection changeDetection の行を消す(v22 は OnPush が既定)
no-input-rename 別名をやめ、プロパティ名をそのまま使う
no-output-on-prefix・no-output-native saved・clicked のような名前にする
no-explicit-any 型を書く(data?: User)
prefer-inject private http = inject(HttpClient)。ng generate @angular/core:inject で一括移行もできる
use-lifecycle-interface implements OnInit(--fix で直る)
no-empty-lifecycle-method 空のメソッドを消す

--fix で変わったところ

-import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output, OnInit } from '@angular/core';
 ...
-export class Bad implements OnDestroy {
+export class Bad implements OnDestroy, OnInit {
-<input ([value])="name">
+<input [(value)]="name">

--fix の後の ng lint は ✖ 16 problems (16 errors, 0 warnings) でした。残りは手で直します。

直した後(指摘0件)

// good.ts
import { Component, OnInit, inject, input, output } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { AsyncPipe } from '@angular/common';
import { Observable, of } from 'rxjs';

interface User {
  id: number;
  name: string;
}

@Component({
  selector: 'app-good',
  imports: [AsyncPipe],
  templateUrl: './good.html',
})
export class Good implements OnInit {
  private http = inject(HttpClient);
  readonly userName = input('');
  readonly saved = output<void>();
  readonly clicked = output<void>();
  count = 0;
  loading$: Observable<boolean> = of(false);
  data?: User;

  ngOnInit(): void {
    this.http.get<User>('/api/me').subscribe((u) => (this.data = u));
  }
}
<!-- good.html -->
@if (count === 0) {
  <div>ゼロ</div>
}
<input [value]="userName()" />
<img src="logo.png" alt="ロゴ" />
<button type="button" (click)="saved.emit()">保存</button>
@if ((loading$ | async) === false) {
  <p>読み込み完了</p>
}
<label for="name">名前</label><input id="name" type="text" />
Linting "demo"...

All files pass linting.

@if などの制御フローは、関連記事「Angular @if・@for・@switchの使い方|ngIfとの対応表」で扱っています。

ルールを足す:型情報が要るルールに注意

既定に入っていないルールを足す例です。

// eslint.config.js の .ts ブロックの rules に追加
'@angular-eslint/no-uncalled-signals': 'error',            // signal を () なしで条件に使う誤り
'no-console': ['warn', { allow: ['warn', 'error'] }],       // console.log を警告に

// .html ブロックの rules に追加
'@angular-eslint/template/button-has-type': 'error',        // <button> に type を
'@angular-eslint/template/prefer-self-closing-tags': 'error', // <app-x></app-x> → <app-x />

no-uncalled-signals を足しただけで ng lint を実行すると、次のエラーで止まりました。

Error when running ESLint: Error while loading rule '@angular-eslint/no-uncalled-signals': You have used a rule which requires type information, but don't have parserOptions set to generate type information for this file. See https://tseslint.com/typed-linting for enabling linting with type information.

型情報(TypeScript の型チェックの結果)を使うルールは、parserOptions の設定が要ります。 .ts のブロックに次を足すと動きました。

    processor: angular.processInlineTemplates,
    languageOptions: {
      parserOptions: {
        projectService: true,      // tsconfig を自動で探して型情報を作る
        tsconfigRootDir: __dirname,
      },
    },

足したルールで出た指摘です(インラインテンプレートの中も調べられています)。

src/app/bad/extra.ts
  14:9  error    Doing logic operations on signals will give unexpected results, you probably want to invoke the signal to get its value  @angular-eslint/no-uncalled-signals
  15:7  warning  Unexpected console statement. Only these console methods are allowed: warn, error  no-console
  6:5   error    Type for <button> is missing  @angular-eslint/template/button-has-type
  7:11  error    Use built-in control flow instead of directive ngIf  @angular-eslint/template/prefer-control-flow
  8:16  error    Use self-closing tags for elements with a closing tag but no content  @angular-eslint/template/prefer-self-closing-tags

14行目は if (this.open) { … } で、this.open() と書くべきところでした。signal は関数なので、() を付けないと常に真になります。

型情報を使うと、検査は遅くなります。ng new 直後の小さなプロジェクトで ng lint 全体の時間を測ると、型情報なしで約2.8秒、ありで約6.4秒でした。

Prettier との併用

Angular 22 の ng new は、prettier を devDependencies に入れ、.prettierrc(printWidth: 100・singleQuote: true、HTML は angular パーサー)を作ります。angular-eslint の README には「コードの書式(フォーマット)のルールは持たない」とあり、書式は Prettier に任せる分担です。

生成された設定に、Prettier とぶつかるルールがあるかを、eslint-config-prettier 10.1.8 の確認コマンドで調べました。

npx eslint-config-prettier src/app/app.ts src/app/app.html
The following rules are enabled but cannot be automatically checked. See:
https://github.com/prettier/eslint-config-prettier#special-rules

- no-unexpected-multiline

Other than that, no rules that are unnecessary or conflict with Prettier were found.

生成された設定には、Prettier とぶつかるルールは見つかりませんでした(no-unexpected-multiline は、自動では判定できない特別なルールとして挙がっただけです)。今後、書式に関わるルールを足すときのために、最後に eslint-config-prettier を入れておく形が安全です。

npm i -D eslint-config-prettier
const prettier = require('eslint-config-prettier/flat');

module.exports = defineConfig([
  // …生成された2つのブロック…
  prettier, // 最後に置く:Prettier と重なるルールを切る
]);

この形で ng lint を実行し、All files pass linting. になることを確かめました。書式の検査は ESLint ではなく Prettier で別に行います。

"scripts": {
  "lint": "ng lint",
  "format": "prettier --write \"src/**/*.{ts,html,scss}\"",
  "format:check": "prettier --check \"src/**/*.{ts,html,scss}\""
}

ng new 直後のファイルに prettier --check をかけると、main.ts など5つのファイルが [warn] になりました。導入したら一度 --write で整えておくと、以後の差分が書式の変更で埋もれません。

ng lint のオプション(よく使うもの)

ng lint --help で出た一覧から、よく使うものを抜き出しました。

オプション 既定 用途
--fix false 自動で直せるものを直す
--max-warnings <数> -1(無制限) 警告がこの数を超えたら失敗にする
--quiet false エラーだけ表示する
--format <形式> stylish json など ESLint の出力形式
--output-file <パス> なし 結果をファイルに書く
--cache false 変わったファイルだけ調べる(.eslintcache を作る)
--force false エラーがあっても成功で終える
--lint-file-patterns angular.json の値 調べるファイルを指定する

警告1件だけのファイルで終了コードを比べた結果です。

コマンド 終了コード
ng lint 0(警告だけなら成功)
ng lint --max-warnings 0 1(Found 1 warnings, which exceeds your configured limit (0).)
ng lint --format json --output-file lint.json 0(結果は JSON に書かれた)

CI での実行

CI(自動テストの仕組み)では、エラーで止めるだけでなく、警告も --max-warnings 0 で止めると、警告が増えていくのを防げます。GitHub Actions の例です。

# .github/workflows/lint.yml
name: lint
on: [push, pull_request]
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npx ng lint --max-warnings 0
      - run: npx prettier --check "src/**/*.{ts,html,scss}"
  • ng lint はエラーがあると終了コード 1 を返すので、そのステップで CI が失敗します
  • 結果をファイルで残したいときは --format json --output-file lint.json を付けて、成果物として保存します
  • Angular CLI 22.2.2 を Node.js 22.22.2 で動かすと The Angular CLI requires a minimum Node.js version of v22.22.3 or v24.15.0 or v26.0.0. と出て止まりました。CI の node-version はこの条件に合わせます

GitHub Actions の基本は、関連記事「CI/CDとは|GitHub Actionsで最初のパイプラインを作るまで」で扱っています。

全ルール一覧(93個)

angular-eslint 22.6.0 に入っている全ルールです。「推奨」は既定で有効なもの、「自動修正」は --fix で直せるものです。

TypeScript 側(51個)

ルール(@angular-eslint/ を省略) 推奨 自動修正 内容
component-class-suffix クラス名の末尾を Component に(v20 以降は勧められていない)
component-max-inline-declarations インラインのテンプレート・スタイルの行数の上限
component-selector 生成時 コンポーネントのセレクタ名の決まり
computed-must-return 値を返さない computed()
consistent-component-styles ○ styles・styleUrl(s) の書き方をそろえる
contextual-decorator クラスの種類に合わないデコレーター
contextual-lifecycle ○ クラスの種類に合わないライフサイクル
directive-class-suffix クラス名の末尾を Directive に(v20 以降は勧められていない)
directive-selector 生成時 ディレクティブのセレクタ名の決まり
inject-at-top inject() をクラスの先頭にまとめる
no-async-lifecycle-method async ngOnInit() など(Angular は待たない)
no-attribute-decorator @Attribute を使わない
no-developer-preview 開発者プレビューの API を使わない
no-duplicates-in-metadata-arrays imports などの重複
no-empty-lifecycle-method ○ 空のライフサイクル
no-experimental 実験的な API を使わない
no-forward-ref forwardRef を使わない
no-implicit-take-until-destroyed 注入の文脈の外で DestroyRef なしの takeUntilDestroyed()
no-input-prefix 入力名の禁止した接頭辞
no-input-rename ○ ○ 入力の別名
no-inputs-metadata-property ○ inputs メタデータ
no-lifecycle-call ライフサイクルを自分で呼ぶ
no-output-native ○ DOM イベント名の出力
no-output-on-prefix ○ on で始まる出力名
no-output-rename ○ ○ 出力の別名
no-outputs-metadata-property ○ outputs メタデータ
no-pipe-impure pure: false の Pipe
no-queries-metadata-property queries メタデータ
no-uncalled-signals () なしで signal を条件に使う(型情報が要る)
pipe-prefix Pipe 名の接頭辞
prefer-host-metadata-property @HostBinding・@HostListener より host
prefer-inject ○ コンストラクタ DI より inject()
prefer-on-push-component-change-detection ○ ○ OnPush をやめない
prefer-output-emitter-ref @Output() より output()
prefer-output-readonly 出力を readonly に
prefer-service-decorator ○ @Injectable({ providedIn: 'root' }) より @Service()
prefer-signal-model ○ input と output の組より model
prefer-signals ○ @Input()・@ViewChild() などより signal
prefer-standalone ○ ○ standalone: false にしない
reactive-context-must-read-signal signal を1つも読まない computed()・effect()
relative-url-prefix templateUrl などに ./・../ を付ける
require-lifecycle-on-prototype ライフサイクルをインスタンスのプロパティにしない
require-localize-metadata $localize に翻訳用の説明を付ける
runtime-localize $localize を実行時の翻訳で使える形に
sort-keys-in-type-decorator ○ デコレーターのキーの順番
sort-lifecycle-methods ライフサイクルを実行順に並べる
use-component-selector セレクタを書く
use-component-view-encapsulation ViewEncapsulation.None を使わない
use-injectable-provided-in providedIn を書く
use-lifecycle-interface ○(warn) ○ ライフサイクルのインターフェース
use-pipe-transform-interface ○ ○ PipeTransform を implements

テンプレート側(42個)

ルール(@angular-eslint/template/ を省略) 推奨 自動修正 内容
alt-text a11y 代わりの文の無い画像など
attributes-order ○ 属性・バインディングの順番
banana-in-box ○ ○ 逆向きの ([x])
button-has-type <button> に type
click-events-have-key-events a11y (click) にキーのイベント
conditional-complexity 条件式の複雑さの上限
cyclomatic-complexity 分岐の数の上限
elements-content a11y 見出し・リンク・ボタンの中身
eqeqeq ○ ○ ===・!== を使う
i18n ○ i18n 属性の付け忘れなど
interactive-supports-focus a11y (click) の要素をフォーカス可能に
label-has-associated-control a11y <label> と入力欄の結び付け
mouse-events-have-key-events a11y マウスのイベントに focus・blur
no-any $any() を使わない
no-autofocus a11y ○ autofocus を使わない
no-call-expression テンプレートでの関数呼び出し(出力の処理を除く)
no-distracting-elements a11y ○ <marquee>・<blink>
no-duplicate-attributes 同じ入力・イベントの重複
no-empty-control-flow 中身の無い @if など
no-inline-styles style="…"
no-interpolation-in-attributes ○ 属性の {{ }} よりプロパティバインディング
no-negated-async ○ async パイプの否定
no-nested-tags <p> の中の <p>、<a> の中の <a>
no-non-null-assertion テンプレートの !
no-outerhtml outerHTML を使わない
no-positive-tabindex 正の tabindex
prefer-at-else ○ 逆条件の @if より @else
prefer-at-empty ○ 別の @if より @for の @empty
prefer-built-in-pipes toLowerCase() などより組み込みパイプ
prefer-class-binding ngClass より [class]
prefer-contextual-for-variables ○ @for の $index などを使う
prefer-control-flow ○ *ngIf などより @if
prefer-ngsrc <img src> より ngSrc
prefer-self-closing-tags ○ 中身の無い要素を <x /> に
prefer-static-string-properties ○ 固定の文字列はバインディングにしない
prefer-style-binding ngStyle より [style]
prefer-template-literal ○ 文字列の連結よりテンプレートリテラル
require-switch-default @switch に @default
role-has-required-aria a11y role に必要な aria-*
table-scope a11y ○ scope は <th> だけ
use-track-by-function trackBy を使う
valid-aria a11y 正しい aria-*

「内容」は各ルールの meta 情報(英語の説明)を短く訳したものです。「自動修正」は meta で fixable とされているもので、書き方によっては直りません(上の例の eqeqeq は --fix の後も残りました)。

よくある質問

Q. .eslintrc.json で書かれた設定を使いたいです。 A. angular-eslint の README では、flat config(eslint.config.js)だけに対応するとされています。.eslintrc.json の extends・overrides は、上の生成例のように files ごとのブロックと extends の配列に書き換えます。

Q. 一部のファイルだけ検査から外したいです。 A. defineConfig([...]) の先頭に { ignores: ['src/legacy/**'] } のブロックを置きます。angular.json の lintFilePatterns を狭める方法もあります。

Q. ルールを1行だけ無効にしたいです。 A. TypeScript では // eslint-disable-next-line @angular-eslint/no-input-rename、HTML では <!-- eslint-disable-next-line @angular-eslint/template/alt-text --> のように、ルール名を付けて書きます。2つの <img> の片方の前にだけ HTML のコメントを書くと、指摘はもう片方の1件だけになりました。ルール名を書かないと、その行の全ルールが外れます。

Q. VS Code で保存時に直したいです。 A. ESLint の拡張機能を入れ、設定の editor.codeActionsOnSave に "source.fixAll.eslint": "explicit" を書きます。HTML も調べるには eslint.validate に html を入れます。

まとめ

  • Angular 22 の新規プロジェクトには lint が無い。ng add angular-eslint で eslint.config.js と lint ターゲットが作られた
  • 設定は flat config。.ts と .html のブロックに分かれ、Angular 用のルールは既定で28個(セレクタ名の2つを足して30個)
  • わざと間違えたコードで18件の指摘が出て、--fix で直ったのは2件。残りは表のとおり手で直す
  • 型情報を使うルール(no-uncalled-signals など)は projectService: true が要る。無いとエラーで止まった
  • 生成された設定には Prettier とぶつかるルールは無かった。念のため最後に eslint-config-prettier/flat を置く
  • CI では ng lint --max-warnings 0 で警告も止める

参考・出典

確認日はいずれも 2026年10月13日です。

  • Angular「ng lint」:https://angular.dev/cli/lint
  • angular-eslint(GitHub リポジトリ・README):https://github.com/angular-eslint/angular-eslint
  • angular-eslint のルール一覧(eslint-plugin):https://github.com/angular-eslint/angular-eslint/tree/main/packages/eslint-plugin/docs/rules
  • angular-eslint のルール一覧(eslint-plugin-template):https://github.com/angular-eslint/angular-eslint/tree/main/packages/eslint-plugin-template/docs/rules
  • typescript-eslint「Linting with Type Information」:https://typescript-eslint.io/getting-started/typed-linting
  • eslint-config-prettier:https://github.com/prettier/eslint-config-prettier
  • ESLint「Configuration Files」:https://eslint.org/docs/latest/use/configure/configuration-files

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「angular-eslintの設定|ng lintの導入とルール一覧」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?