Microsoft developer platform関連のTypeScript/Reactテンプレートを使っている場合、今回まず確認すべき点は、ESLint設定で使われていた非推奨のtseslint.config()が、ESLint本体のdefineConfig()へ置き換えられたことです。既存アプリがすぐ壊れるタイプの変更ではありませんが、.NET AspireやMicrosoft系テンプレートを参考にしてフロントエンド環境を作っているチームは、eslint.config.jsやeslint.config.mjsの書き方を見直すタイミングです。
この変更は、Microsoftのmicrosoft/aspireリポジトリのPull Request「fix: migrate from deprecated tseslint.config to eslint/config」で行われました。PRでは、非推奨のtseslint.config()からESLint coreのdefineConfig()へ移行し、あわせてglobalIgnores()の利用や一部設定の簡素化が行われています。対象は主にAspireのTypeScript/React系テンプレートやスキャフォールド設定であり、アプリケーションの業務ロジック変更ではなく、開発環境・Lint設定の保守性を高める更新と捉えるのが適切です。(GitHub)
Microsoft developer platform documentation updateで何が変わったのか
今回のMicrosoft developer platform documentation updateの要点は、TypeScript向けESLint設定の記述方法が、typescript-eslint独自のヘルパーからESLint公式のヘルパーへ寄せられたことです。
変更前は、ESLintのFlat Configで次のような形が使われていました。
import tseslint from 'typescript-eslint';
export default tseslint.config(
{ ignores: ['dist'] },
{
// ESLint設定
}
);
変更後は、ESLint本体のeslint/configからdefineConfigとglobalIgnoresを読み込みます。
import { defineConfig, globalIgnores } from 'eslint/config';
import tseslint from 'typescript-eslint';
export default defineConfig(
globalIgnores(['dist']),
{
extends: [js.configs.recommended, tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
// ESLint設定
}
);
typescript-eslintの公式ドキュメントでも、configはdeprecated、つまり非推奨として扱われており、ESLint coreのdefineConfig()を推奨する説明に変わっています。defineConfig()はESLint v9.22.0で導入されたESLint本体のヘルパーで、typescript-eslint側のtseslint.config()に近い使い勝手を提供します。(TypeScript ESLint)
今回の変更点を実務目線で整理
PRで確認できる主な変更は、次の3つです。単に関数名が変わっただけではなく、チームでESLint設定を読むときの分かりやすさにも影響します。
| 変更点 | 変更前 | 変更後 | 確認すべきこと |
|---|---|---|---|
| ESLint設定ヘルパー | tseslint.config() | defineConfig() | eslint/configから正しくimportしているか |
| 無視対象の指定 | { ignores: ['dist'] } | globalIgnores(['dist']) | distやbuildなどの除外対象が意図通りグローバルに効くか |
| TypeScript設定の一部簡素化 | tsconfigRootDir: import.meta.dirnameを明示 | 一部テンプレートから削除 | 型情報付きLintが想定通り動くか |
PRでは、AspireのTypeScript/Reactスターターテンプレート、Pythonスターターのフロントエンドテンプレート、CLIのTypeScriptスターター、TypeScript AppHost用のスキャフォールド設定、playground内のESLint設定など、複数のeslint.config.*ファイルが更新されています。(GitHub)
tseslint.config()からdefineConfig()へ移行する理由
tseslint.config()は、TypeScriptをESLintで扱うための便利なFlat Config用ヘルパーでした。しかし現在は、ESLint本体がeslint/configからdefineConfig()を提供しているため、設定ヘルパーをESLint公式の仕組みに揃える流れになっています。
ESLint公式ブログでは、Flat Config移行後に寄せられた課題として、TypeScriptでの扱いにくさ、設定の拡張の難しさ、global ignoresの分かりにくさが挙げられています。その改善策としてdefineConfig()やglobalIgnores()が導入されました。(ESLint)
実務上のメリットは、次の3点です。
- ESLint本体の標準的な書き方に揃えられる
- Flat Configの型安全性や補完を使いやすくなる
ignoresの意図をglobalIgnores()で明確にできる
特にチーム開発では、「このignoresは全体除外なのか、特定設定内だけの除外なのか」が分かりにくいと、Lint対象が想定より広がったり狭まったりします。globalIgnores(['dist'])のように書くことで、ビルド成果物を全体で無視する意図が読み取りやすくなります。
影響を受ける可能性がある人
今回の変更は、Microsoft developer platformを利用するすべてのユーザーに大きな影響が出るものではありません。影響があるかどうかは、AspireのテンプレートやTypeScript/ReactフロントエンドのESLint設定をどの程度利用しているかで判断します。
| 対象者・チーム | 影響度 | 対応の目安 |
|---|---|---|
| 最新のAspireテンプレートから新規プロジェクトを作る人 | 低 | 基本的に新しい設定をそのまま利用 |
| 既存のAspireテンプレートをコピーして運用しているチーム | 中 | eslint.config.*内のtseslint.config()を確認 |
| 独自テンプレートでAspireのサンプルを参考にしているチーム | 中 | サンプルとの差分を確認し、設定を更新 |
| TypeScript AppHostやスキャフォールド設定を保守しているチーム | 中〜高 | 生成されるESLint設定まで確認 |
| ESLint v9以降への移行を進めているチーム | 高 | defineConfig()前提の設定へ揃える |
既存プロジェクトでtseslint.config()がまだ動いている場合でも、「動くから問題ない」と放置するのは避けたいところです。typescript-eslint側ではconfigが非推奨として説明されており、今後の更新で警告や互換性確認の負担が増える可能性があります。(TypeScript ESLint)
自分のプロジェクトで確認すべきファイル
まず確認する場所は、プロジェクトルートやフロントエンドディレクトリにあるESLint設定ファイルです。ESLint公式ドキュメントでは、Flat Configの設定ファイル名としてeslint.config.js、eslint.config.mjs、eslint.config.cjsなどが挙げられています。(ESLint)
確認対象の例は次のとおりです。
eslint.config.js
eslint.config.mjs
frontend/eslint.config.js
src/**/eslint.config.js
templates/**/eslint.config.js
検索するときは、次の文字列を探すと早く判断できます。
tseslint.config(
{ ignores:
tsconfigRootDir: import.meta.dirname
...tseslint.configs.recommended
特にテンプレートやサンプルを社内で管理している場合は、実アプリだけでなく「これから生成されるプロジェクト」の設定も確認してください。生成元が古いままだと、新規プロジェクトを作るたびに非推奨設定が再利用されます。
移行手順:tseslint.config()をdefineConfig()へ置き換える
既存プロジェクトで同様の設定を使っている場合、基本的な移行は次の流れで進めます。
| 手順 | 作業内容 | 失敗しやすいポイント |
|---|---|---|
| 1 | ESLintとtypescript-eslintのバージョンを確認 | 古いESLintでeslint/configが使えない場合がある |
| 2 | defineConfigをimportする | typescript-eslintからではなくeslint/configから読み込む |
| 3 | tseslint.config()をdefineConfig()へ変更 | 引数の配列・オブジェクト構造を崩さない |
| 4 | グローバル除外をglobalIgnores()へ変更 | ローカル除外とグローバル除外を混同しない |
| 5 | npm run lintなどで検証 | Lint対象ファイルが増減していないか確認する |
| 6 | CIとエディタ上のLintを確認 | CIだけ通ってVS Codeなどで警告が出ないケースに注意 |
移行前の例
import js from '@eslint/js';
import globals from 'globals';
import tseslint from 'typescript-eslint';
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
}
);
移行後の例
import js from '@eslint/js';
import { defineConfig, globalIgnores } from 'eslint/config';
import globals from 'globals';
import tseslint from 'typescript-eslint';
export default defineConfig(
globalIgnores(['dist']),
{
extends: [js.configs.recommended, tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
globals: globals.browser,
},
}
);
PR内でも、{ ignores: ['dist'] }がglobalIgnores(['dist'])に変更され、extends内の...tseslint.configs.recommendedがtseslint.configs.recommendedに整理されています。また、ESLint Flat ConfigではecmaVersionの既定値が"latest"であるため、明示的なecmaVersion: 2020を見直す観点も示されています。(GitHub)
globalIgnores()で注意すべきこと
globalIgnores()は、Lint対象から完全に除外したいファイルやディレクトリを明示するためのヘルパーです。ESLint公式ドキュメントでも、混乱を避けるためにグローバルな無視設定にはglobalIgnores()を使う例が示されています。(ESLint)
よくある除外対象は次のようなものです。
globalIgnores([
'dist',
'build',
'coverage',
'.next',
]);
ただし、何でも除外すればよいわけではありません。たとえばsrc/generatedのような生成コードを除外する場合、本当にレビュー不要なのか、型生成後のコード品質を確認しなくてよいのかをチームで決めておく必要があります。
判断基準はシンプルです。
| 除外してよいもの | 除外に慎重になるもの |
|---|---|
| ビルド成果物 | 手で編集する可能性がある生成コード |
| カバレッジ出力 | アプリ本体に含まれるコード |
| 一時ファイル | テストコード |
| パッケージ出力先 | 共有ライブラリのソース |
distやbuildのような成果物は除外しても問題になりにくい一方、src配下のファイルを広く除外すると、Lintが本来検出すべき問題を見逃す可能性があります。
tsconfigRootDir削除はどう考えるべきか
今回のPRでは、一部のTypeScript AppHost向けESLint設定からtsconfigRootDir: import.meta.dirnameが削除されています。PR内の説明では、typescript-eslint側の改善により、ESLint設定ファイルのパスから推論できるようになったためとされています。(GitHub)
ただし、すべてのプロジェクトで無条件に削除してよいとは限りません。特に次のような構成では、削除後に型情報付きLintが期待通り動くか確認してください。
- モノレポで複数の
tsconfig.jsonを使っている frontend、backend、packagesなどに設定が分かれている- ESLint設定ファイルをプロジェクトルート以外に置いている
parserOptions.projectServiceや型情報付きルールを使っている- CIの実行ディレクトリとローカルの実行ディレクトリが異なる
確認方法としては、単にnpm run lintが通るかだけでなく、型情報を使うルールが実際に動いているかを見ます。たとえば@typescript-eslint/no-floating-promisesのような型情報に依存するルールを使っている場合、設定変更後も検出結果が変わらないかを確認すると安全です。
既存プロジェクトで急いで対応すべきか
結論として、現在Lintが動いている既存プロジェクトであれば、緊急障害対応のように即日修正する必要はないケースが多いです。ただし、次のいずれかに当てはまる場合は早めに対応した方がよいでしょう。
| 状況 | 推奨対応 |
|---|---|
| ESLintやtypescript-eslintを近いうちに更新する | 先にdefineConfig()へ移行 |
| 新規プロジェクト用テンプレートを配布している | テンプレート側を優先更新 |
| CIで非推奨警告が出ている | 警告を放置せず設定を更新 |
| Aspireのサンプルやテンプレートを教材・社内標準にしている | ドキュメントとサンプルを更新 |
| Flat Config移行中 | defineConfig()前提で統一 |
逆に、ESLint v8以前の古い構成や.eslintrcを使っているプロジェクトでは、今回の変更だけを切り出して適用するより、Flat Config移行全体として設計した方が安全です。設定ファイル形式、プラグイン、エディタ連携、CIコマンドをまとめて見直す必要があります。
移行時に起きやすいトラブルと対処法
Cannot find module 'eslint/config'が出る
eslint/configが見つからない場合、ESLintのバージョンが古い可能性があります。まずpackage.jsonとロックファイルを確認し、ESLint本体がdefineConfig()を利用できるバージョンかを確認してください。typescript-eslint公式ドキュメントでは、defineConfig()はESLint v9.22.0で初めてリリースされたと説明されています。(TypeScript ESLint)
すぐにESLintを更新できない場合は、ESLint公式ブログで触れられている@eslint/config-helpersの利用も選択肢になります。ただし、チームの依存関係を増やすことになるため、長期的にはESLint本体を更新する方が管理しやすいでしょう。(ESLint)
Lint対象が変わってしまう
ignoresをglobalIgnores()へ変更した後、Lint対象が変わることがあります。これは、以前のignoresがグローバル除外として効いていたのか、特定の設定オブジェクト内だけで効いていたのかを誤解している場合に起こります。
対処として、移行前後で次のコマンドの結果を比較します。
npm run lint
npx eslint .
npx eslint frontend
エラー件数だけで判断せず、「どのファイルが新たに対象になったか」「逆に対象外になっていないか」を確認してください。
extendsの扱いで設定が崩れる
defineConfig()では、設定配列やネストした設定を扱いやすくするための仕組みが用意されています。ESLint公式ブログでは、defineConfig()が引数を自動的にフラット化し、スプレッド演算子を使う混乱を減らす説明がされています。(ESLint)
ただし、複雑な共有設定や独自プラグインを組み合わせている場合は、単純な置換で済まないことがあります。特定のfilesにだけルールを適用している構成では、移行後に対象範囲が変わっていないかを重点的に見てください。
エディタでは動かないがCIでは動く
ESLint設定を更新した後、CIでは通るのにVS Codeなどのエディタ上でLintが動かないことがあります。この場合は、エディタ拡張が参照している作業ディレクトリ、ESLintのバージョン、ワークスペース設定を確認します。
モノレポでは、エディタがルートのESLintではなくサブディレクトリ側の設定を見に行くことがあります。frontendだけにpackage.jsonがある構成では、エディタのESLint working directories設定も確認対象です。
Microsoft developer platform利用者が取るべき確認手順
今回の更新を受けて、Microsoft developer platformやAspire関連の開発環境を保守しているチームは、次の順序で確認すると効率的です。
| 優先度 | 確認項目 | 具体的な作業 |
|---|---|---|
| 高 | tseslint.config()の使用有無 | リポジトリ全体検索で洗い出す |
| 高 | テンプレート内のESLint設定 | templates、scaffold、starter配下を確認 |
| 高 | CIのLint実行 | 移行後に同じコマンドで通るか確認 |
| 中 | ignoresの扱い | globalIgnores()にすべき除外か判断 |
| 中 | tsconfigRootDirの必要性 | モノレポやサブディレクトリ構成で検証 |
| 中 | 社内ドキュメント | サンプルコードをdefineConfig()前提に更新 |
| 低 | 既存アプリのルール最適化 | 警告数や対象ファイルを見直す |
社内でMicrosoft系テンプレートをベースにしたプロジェクト作成手順書を持っている場合は、記事やドキュメントのコード例も更新してください。開発者が古いtseslint.config()の例をコピーし続けると、リポジトリごとに設定の世代が分かれ、保守コストが上がります。
コードレビューで見るべきポイント
この種の変更は「Lint設定の軽微な修正」として扱われがちですが、レビューでは次の観点を押さえると失敗を防げます。
| レビュー観点 | 確認内容 |
|---|---|
| import元 | defineConfigとglobalIgnoresがeslint/configから読み込まれているか |
| 除外対象 | distなどの成果物だけを除外しているか |
| 対象ファイル | files: ['**/*.{ts,tsx}']などが維持されているか |
| 共有設定 | tseslint.configs.recommendedの扱いが正しいか |
| 型情報付きLint | projectService利用時に検出結果が変わっていないか |
| CI | ローカルとCIで同じLint結果になるか |
レビューで特に見落としやすいのは、ignoresの意味の変化です。globalIgnores()へ置き換えることで意図は明確になりますが、もともとの設定が本当にグローバル除外だったのかは必ず確認してください。
今回の更新をどう活用すべきか
今回のMicrosoft developer platform documentation updateは、単なるESLint設定の差し替えではなく、TypeScript開発環境を現在のESLint標準に寄せるサインと考えると実務で役立ちます。
特に、次のようなチームでは対応価値が高いです。
- AspireやMicrosoft系サンプルをベースにフロントエンドを作っている
- TypeScript/Reactテンプレートを社内で複製している
- ESLint Flat Configへ移行したばかりで設定が複雑になっている
- Lint設定の警告や非推奨項目を減らしたい
- 新規プロジェクトの初期設定を長く使える形にしたい
まずはリポジトリ全体でtseslint.config(を検索し、該当する設定ファイルを一覧化しましょう。次に、defineConfig()とglobalIgnores()へ置き換え、Lint対象が変わっていないかをCIとローカルの両方で確認します。テンプレートや社内ドキュメントまで更新できれば、今後作成するプロジェクトにも同じ改善を反映できます。
Microsoft developer platform関連の更新は、機能追加だけでなく、このような開発環境の標準化にも注意が必要です。今回の変更をきっかけに、ESLint設定を「動いているからそのまま」ではなく、「チームが読みやすく、将来のアップデートに追従しやすい設定」へ整理しておくと、後の移行コストを抑えられます。

コメント