rdlabo/eslint-plugin-rules:私が欲しかったAngularプロジェクトのESLintプラグイン
私が欲しかったAngularプロジェクトのESLintプラグイン
チームでAngular開発をしていると、「この実装ミス自動検出してほしいな」「この書き方を強制してくれないかな」と悩むことありますよね。私の悩みを解決するために @rdlabo/eslint-plugin-rules
を作りました。私がプロジェクトで必要なルールだったルールまとめて、公開した形です。
例えば、Signalパターンへの移行をしていると、マイグレーションのミスって結構起こりがちなんですよね。ちなみに私が見逃しがちなのはこのパターンです。
name = signal<string | undefined>('Angular');
// ❌ WritabeSignal<string>を確認してしまってる
if (name) { ... }
// ✅ 正しくはこちら
if (name()) { ... }
name
は WritabeSignal<string>
なので、条件文でみると値が空やundefinedでも必ず true
を返します。構文的には正しいのでコンパイルエラーも起こさないし、テストや動作確認でようやく気づくレベルの不具合です。まじしんどかったので、これを検出するルールもつくりました。あります。ルール集なので、チームやプロジェクトによって合う・合わないはあると思いますが、もし使えそうなルールがあったら使ってもらえると嬉しいです。
利用可能なルール
このプラグインでは、以下のカスタム ESLint ルールが提供されています:
@rdlabo/rules/deny-constructor-di
コンポーネントやサービスのコンストラクター内での依存性注入(DI)を防ぐルールです。これは、コンストラクターDIからAngularの inject
関数を使ったDIへの移行を促すためのものです。
// ❌ 従来の書き方
constructor(private http: HttpClient) {}
// ✅ 推奨される書き方
private http = inject(HttpClient);
以前はauto fix機能もあったのですが、 ng generate @angular/core:inject
コマンドがリリースされたので、auto fix機能は削除しました。
@rdlabo/rules/signal-use-as-signal
Angular Signals の適切な使用法を検証するルールです。Signals の値を直接ミューテートしたり、不正な方法で Signal にアクセスしたりするコードを検出します。
// ❌ 不正な使用
this.user().name = data.name;
this.users().push(user);
if (this.user) { /* ... */ }
// ✅ 正しい使用
this.user.update(user => ({ ...user, name: data.name }));
this.users.update(users => [...users, user]);
if (this.user()) { /* ... */ }
以前からこのルールはあったのですが、v20.0.0でこのルールを大幅に見直し、auto fix機能も利用可能にしました。
@rdlabo/rules/signal-use-as-signal-template
Angular Signals をテンプレート内で正しく使用することを強制するルールです。Signal の値にアクセスする際は、テンプレートでも Signal 関数を呼び出す必要があります。
<!-- ❌ 不正な使用 -->
{{ count }}
{{ user.name }}
@if (user) { /* ... */ }
<!-- ✅ 正しい使用 -->
{{ count() }}
{{ user().name }}
@if (user()) { /* ... */ }
v20.0.0で追加した新しいルールです。auto fix機能は現在利用できません( templateUrl
を辿るのでLint対象ファイルでないことから仕様上auto fixできなかった)
@rdlabo/rules/component-property-use-readonly
クラスプロパティに対して readonly
修飾子の使用を強制するルールです。特に Angular コンポーネントのプロパティに対して適用されます。
// ❌ readonly が付いていない
private users: User[];
count: number;
name: string;
// ✅ readonly を追加
private readonly users: User[];
readonly count: number;
readonly name: string;
これは、以下で紹介した Zoneless アプリケーションへの移行戦略に含まれる「全てのコンポーネントプロパティを readonly にする」ことをサポートするためのルールです。v20.0.0で追加された新しいルールで、オートフィックス機能が利用可能です。
@rdlabo/rules/deny-soft-private-modifier
ソフトプライベート修飾子の使用を防ぐルールです。明示的なアクセシビリティ修飾子を推奨します。
// ❌ ソフトプライベート修飾子
private http = inject(HttpClient);
// ✅ ハードプライベート修飾子
#http = inject(HttpClient);
オートフィックス機能も利用可能です。
@rdlabo/rules/deny-element
特定の HTML 要素の使用を制限するルールです。設定によって禁止したい要素を指定できます。主にIonicプロジェクトで、モーダルやポップオーバー要素をテンプレートで直接使用することを禁止し、コントローラーからの使用を推奨するために使っています。
<!-- ❌ テンプレートでの直接使用を禁止(Ionicの場合) -->
<ion-modal>
<ion-content>...</ion-content>
</ion-modal>
<!-- ✅ コントローラーからの使用を推奨 -->
<!-- TypeScriptでModalControllerを使用 -->
@rdlabo/rules/deny-import-from-ionic-module
Ionicプロジェクトで @ionic/angular
からの直接インポートを防ぐルールです。より粒度の細かいパスからインポートすることを推奨します。
オートフィックス機能が利用可能です。
@rdlabo/rules/implements-ionic-lifecycle
Ionicプロジェクトでライフサイクルフック(例: ionViewWillEnter
など)をコンポーネントで使用する際に、対応する TypeScript インターフェース(例: IonViewWillEnter
)をクラスが適切に実装していることを保証するルールです。
// ✅ 正しい実装(Ionicプロジェクトの場合)
export class MyPage implements IonViewWillEnter {
ionViewWillEnter() {
// ライフサイクル処理
}
}
v20.0.0で機能を強化して、ライフサイクルインターフェースの実装を自動的に検出して修正したり、空のクラスから不要なインターフェースを削除したりできるようになりました。オートフィックス機能が利用可能です。
インストール方法と設定方法
プラグインのインストールは簡単です:
npm install @rdlabo/eslint-plugin-rules --save-dev
ただし、注意点があります。もしプロジェクトに angular-eslint
パッケージがインストールされていない場合は、このプラグインをインストールする前に angular-eslint
パッケージを先にインストールする必要があります。
# angular-eslintが未インストールの場合は先にインストール
ng add angular-eslint
プロジェクトにインストールした後、ESLint の設定ファイル(通常 eslint.config.js
)にプラグインと使用したいルールを追加します。設定は、主に TypeScript ファイル(*.ts
)と HTML ファイル(*.html
)に対して行います。
const rdlabo = require('@rdlabo/eslint-plugin-rules');
module.exports = tseslint.config(
{
files: ['*.ts'],
plugins: {
'@rdlabo/rules': rdlabo,
},
rules: {
'@rdlabo/rules/deny-constructor-di': 'error',
'@rdlabo/rules/deny-import-from-ionic-module': 'error',
'@rdlabo/rules/implements-ionic-lifecycle': 'error',
'@rdlabo/rules/deny-soft-private-modifier': 'error',
'@rdlabo/rules/signal-use-as-signal': 'error',
'@rdlabo/rules/signal-use-as-signal-template': 'error',
'@rdlabo/rules/component-property-use-readonly': 'error',
},
},
{
files: ['*.html'],
plugins: {
'@rdlabo/rules': rdlabo,
},
rules: {
// Ionicを使用している場合のみ設定
'@rdlabo/rules/deny-element': [
'error',
{
elements: [
'ion-modal',
'ion-popover',
'ion-toast',
'ion-alert',
'ion-loading',
'ion-picker',
'ion-action-sheet',
],
},
],
},
}
);
まとめ
@rdlabo/eslint-plugin-rules
は、Angular アプリケーションのコード品質と保守性を向上させるためのカスタム ESLint ルールの集合体です。特に v20.0.0 リリースでは、Angular Signals のサポートが強化され、Angular v20 で推奨される Zoneless アプリケーションへの移行を支援するための新しいルールが導入されました。
これらのルールは、Angular 開発における特定のベストプラクティスやアンチパターンを防ぐのに役立ち、多くの場合オートフィックス機能も提供されています。特に以下のような場面で威力を発揮します:
- Angular Signalsの適切な使用法の強制
- コンストラクターDIから
inject()
関数への移行支援 - Zoneless変更検知への移行準備(readonly修飾子の強制)
- TypeScriptのアクセシビリティ修飾子の適切な使用
プロジェクトはオープンソースであり、コントリビューションも受け付けています。Ionicプロジェクトでも追加のルールが利用できるため、幅広いAngularプロジェクトで活用できます。
Angular開発でコード品質を向上させたい方は、ぜひ導入を検討してみてください。
それでは、また。
Discussion