公開ローンチ前のコード整備で出てきた
<a>の警告を黙らせるところから、Lint と Formatter の役割分担、ディレクティブコメントの仕組みまで掘った話です。
この記事でわかること
- Lint(静的解析)と Formatter(整形)は別物だという話
// eslint-disable-next-lineのような コメント が、なぜツールへの命令として効くのか- 「ここだけ lint を止めたい」時の粒度の選び方
- 似た仕組みが他のツールにもある話
きっかけ
Next.js のエラー境界用ファイル app/global-error.tsx で、エラー時にホームへ戻すリンクを置いていた:
"use client";
export default function GlobalError() {
return (
<html lang="ja">
<body>
<h2>エラーが発生しました</h2>
{/* エラー境界では Link の router context が利用不可なため素の <a> を使う */}
<a href="/">ホームへ戻る</a>
</body>
</html>
);
}Next.js のページ内リンクは普通 next/link の <Link> を使います。が、エラー境界(特に <html> を自前で描く global-error)では <Link> の router context が利用できない場合があるため、確実に動く素の <a> を選んでいる。コメントにもそう書いてある。
それでも ESLint は警告してきた:
/app/src/app/global-error.tsx
9:17 warning Do not use an `<a>` element to navigate to `/`.
Use `<Link />` from `next/link` instead. @next/next/no-html-link-for-pages
「<a> 使うな、<Link> 使え」── 普通の場面では正しい指摘だが、ここでは意図的に <a> のはず。
Lint は何をしているのか
似て非なるツールとして Formatter(整形ツール、例: Prettier) があるが、役割は完全に別物:
| Lint (ESLint) | Formatter (Prettier) | |
|---|---|---|
| 役割 | 「これおかしくない?」を指摘 | 「見た目を統一」する |
| 例 | 「使ってない変数があるよ」「<a> じゃなく <Link> 使え」「== じゃなく === 使え」 |
インデント、改行、クォート種類、行幅を強制 |
| 対象 | コードの 意味(バグ/規約違反になりそう) | コードの 見た目(人間が読みやすい形) |
混同しがちだが、Lint は「コード化された人間レビュアー」、Formatter は「機械的な清書ツール」。Next.js プロジェクトは大体この2本立てで動いています。
なぜコメントで挙動制御できるのか
@next/next/no-html-link-for-pages のような ESLint ルールは、コード全体に常に適用されます。今回のように「ここは例外」とピンポイントで伝えたい時、どうするか。
答えは:
{/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
<a href="/">ホームへ戻る</a>コメント記法を借りた「ツールへの命令」
// eslint-disable-next-line @next/next/no-html-link-for-pages
└─────┬──────┘ └────┬────┘ └──────────┬─────────────┘
① ② ③
| 部位 | 意味 |
|---|---|
① eslint-disable-next-line |
次の1行だけ ESLint のチェックを止めて |
| ② (区切り) | |
③ @next/next/no-html-link-for-pages |
黙らせるルール名(Next.js eslint plugin 由来) |
コメント記法を借りた 「ESLint への手紙」 とも言えます。
なぜわざわざコメントを使うのか
設定ファイル(.eslintrc)にも「このファイルだけ無効化」みたいな書き方はあるのに、なぜソース内コメントを使う仕組みが用意されているか:
- シンタックスエラーにならない — ただのコメント扱いなので、古いツール・違うパーサに通しても壊れません
- コードと一緒に Git 管理される — 設定ファイルに書くより、現場で見えるし diff も追えます
- 次の1行だけピンポイント で範囲指定できる ── 設定ファイルだと粒度がファイル単位で粗くなります
- 意図が現場に残る ── 「なぜ disable してるのか」を直近行にコメントで書けます
粒度の選び方
ESLint のディレクティブコメントには複数の粒度があります:
| ディレクティブ | 効果範囲 |
|---|---|
// eslint-disable-next-line |
次の1行だけ |
// eslint-disable-line |
そのコメントが書かれた行だけ(行末コメント) |
/* eslint-disable */ ... /* eslint-enable */ |
範囲指定(block) |
/* eslint-disable */(ファイル冒頭) |
ファイル全体 |
迷ったら disable-next-line がデフォルト。副作用が最小で、意図が明確に残ります。
テストコードや legacy 移植ファイルではファイル全体の disable が正当な場面もあるが、雑に使うと lint が他の問題を見逃します。
似た仕組みは他にもある
「コードの一部だけ静的解析を止めたい」というニーズは普遍的なので、各ツールが自前の コメント記法を借りた制御API を持っています:
| ツール | ディレクティブコメント |
|---|---|
| ESLint | // eslint-disable-next-line ... |
| TypeScript | // @ts-ignore / // @ts-expect-error |
| Prettier | // prettier-ignore |
| Stylelint | /* stylelint-disable */ |
| Pylint (Python) | # pylint: disable=... |
| Rubocop (Ruby) | # rubocop:disable Layout/LineLength |
コメントを使うのは「シンタックスエラーにならない」「他のパーサが壊れない」というメリットを共有しています。
学びポイント
- Lint と Formatter は別物。役割は「意味の指摘」と「見た目の統一」で分かれています
- ディレクティブコメントは「ツールへの手紙」。コメント記法を借りて、ツールに直接命令を渡しています
- disable する時は最小粒度から ──
disable-next-lineが基本、ファイル単位の disable は副作用が大きいです - なぜ disable したかを直近コメントで残す ── 半年後の自分(or 他人)が「なんで lint 切ってんの?」と疑った時の答えがコードに残ります
おわりに
ツールの警告は、コードがおかしい時 / ツールがそのコードの意図を読めない時 の2パターンで出ます。後者の時に「コードを警告に合わせて書き換える」のは、しばしば本来の意図を歪める方向に倒れます。
今回のように「エラー境界では Link が使えない」みたいな事情があるなら、コードを正しく保ったまま ツール側に「ここは例外」と伝える ほうが筋がいいです。そのための仕組みがディレクティブコメントです。
「コメントなのに命令として効く」のは魔法ではなく、ツールがソースを読む時に、コメントまで含めて見ている からこそ成り立つ素直な仕掛けです。
この記事は2026年5月時点の自分の理解で書いています。仕様や認識が変わってる可能性があります。