T-CREATOR

ESLint × TypeScript「Parsing error: Cannot read file 'tsconfig.json'」完全解決ガイド

ESLint × TypeScript「Parsing error: Cannot read file 'tsconfig.json'」完全解決ガイド

TypeScript プロジェクトで ESLint を設定した際に、「Parsing error: Cannot read file 'tsconfig.json'」というエラーに遭遇したことはありませんか。このエラーは、ESLint が TypeScript の設定ファイルを正しく読み込めない場合に発生します。

特に、モノレポ構成や Next.js プロジェクトなど、複数の tsconfig.json ファイルが存在する環境では頻繁に遭遇するエラーです。本記事では、このエラーの原因から具体的な解決方法まで、初心者の方にもわかりやすく解説していきますね。

背景

ESLint と TypeScript の連携の仕組み

ESLint で TypeScript のコードを解析するには、@typescript-eslint​/​parserというパーサーを使用します。このパーサーは、TypeScript のコンパイラ(TypeScript Compiler API)を内部で利用しており、型情報を含めた高度な静的解析を実現しています。

型情報を活用することで、単純な構文チェックだけでなく、型安全性に関わるルールも適用できるようになります。これが TypeScript と ESLint を組み合わせる大きなメリットですね。

tsconfig.json の役割

tsconfig.jsonは、TypeScript コンパイラの設定ファイルです。このファイルには、以下のような重要な情報が含まれています。

typescript{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

@typescript-eslint​/​parserは、この設定ファイルを読み込むことで、プロジェクトの TypeScript 環境を正しく理解します。どのファイルが解析対象なのか、どのような型チェックルールが適用されているのかを把握できるのです。

以下の図は、ESLint が TypeScript コードを解析する際のデータフローを示しています。

mermaidflowchart LR
  code["TypeScriptコード"] -->|読み込み| eslint["ESLint"]
  eslint -->|解析依頼| parser["@typescript-eslint/parser"]
  parser -->|設定読み込み| tsconfig["tsconfig.json"]
  parser -->|型情報取得| tsapi["TypeScript Compiler API"]
  tsapi -->|解析結果| parser
  parser -->|AST| eslint
  eslint -->|検証| rules["ESLintルール"]
  rules -->|結果| output["エラー/警告"]

図から分かるように、tsconfig.jsonは TypeScript 解析の核となる設定ファイルです。このファイルが正しく読み込めないと、ESLint は適切な型情報を取得できず、エラーが発生してしまいます。

課題

エラーの詳細

このエラーが発生すると、以下のようなメッセージが表示されます。

textParsing error: Cannot read file 'tsconfig.json'

より詳細なエラーメッセージでは、以下のような内容が含まれることもあります。

textParsing error: Cannot read file '/path/to/your/project/tsconfig.json'.
Error: ENOENT: no such file or directory, open '/path/to/your/project/tsconfig.json'

このエラーには、主に以下のようなバリエーションがあります。

#エラーパターン意味
1ENOENT: no such file or directory指定されたパスにファイルが存在しない
2Cannot read property of undefinedtsconfig.json のパス解決が失敗している
3The file does not match your project configtsconfig.json の include 設定に該当しない

エラーが発生する主な原因

このエラーは、以下のような状況で発生しやすいです。

ESLint 設定ファイルでのパス指定ミス .eslintrc.js.eslintrc.json内で、parserOptions.projectに指定したパスが間違っている場合です。相対パスの基準点を誤解していることが多いですね。

作業ディレクトリとの不一致 ESLint を実行する際の作業ディレクトリと、設定ファイルで想定しているディレクトリが異なる場合に発生します。これはモノレポ構成で特に起こりやすい問題です。

複数の tsconfig.json ファイルの管理 プロジェクト内に複数のtsconfig.jsonファイル(例:tsconfig.jsontsconfig.eslint.jsontsconfig.build.json)が存在し、正しいファイルを参照できていない場合です。

以下の図は、エラーが発生する典型的な状況を示しています。

mermaidflowchart TD
  start["ESLint実行"] --> check["tsconfig.jsonを<br/>探す"]
  check --> path1{".eslintrc.jsの<br/>project設定"}
  path1 -->|相対パス| resolve["パス解決"]
  resolve --> exists{ファイル<br/>存在?}
  exists -->|はい| success["解析成功"]
  exists -->|いいえ| error["Parsing error:<br/>Cannot read file"]
  path1 -->|未設定| auto["自動検出"]
  auto --> autoexists{ファイル<br/>存在?}
  autoexists -->|はい| success
  autoexists -->|いいえ| error

このように、パスの解決プロセスのどこかで失敗すると、エラーが発生してしまいます。

解決策

基本的な解決アプローチ

エラーを解決するには、ESLint が tsconfig.json を正しく見つけられるようにする必要があります。以下では、段階的に解決方法を説明していきますね。

解決方法 1:parserOptions.project の設定を確認

最も基本的な解決方法は、.eslintrc.js(または.eslintrc.json)のparserOptions.project設定を正しく指定することです。

まず、現在の設定を確認しましょう。

javascript// .eslintrc.js
module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: './tsconfig.json', // この行を確認
  },
  // その他の設定...
};

このprojectの値は、ESLint 設定ファイル(.eslintrc.js)からの相対パスとして解釈されます。もし.eslintrc.jsがプロジェクトルートにあり、tsconfig.jsonも同じ場所にある場合は、以下のように指定します。

javascriptparserOptions: {
  project: './tsconfig.json',
}

もしモノレポ構成で、ESLint 設定ファイルと tsconfig.json が異なるディレクトリにある場合は、適切な相対パスを指定してください。

javascript// packages/app/.eslintrc.js の場合
parserOptions: {
  project: './tsconfig.json', // packages/app/tsconfig.json を指す
}

解決方法 2:絶対パスを使用する

相対パスの解決が複雑な場合は、絶対パスを使用すると確実です。Node.js のpathモジュールを活用しましょう。

javascript// .eslintrc.js
const path = require('path');

module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: path.resolve(__dirname, './tsconfig.json'),
  },
  // その他の設定...
};

__dirnameは、.eslintrc.jsファイルが存在するディレクトリの絶対パスを表します。path.resolve()を使用することで、確実に正しいパスを生成できますね。

複数の tsconfig.json ファイルがある場合は、配列で指定することも可能です。

javascriptparserOptions: {
  project: [
    path.resolve(__dirname, './tsconfig.json'),
    path.resolve(__dirname, './tsconfig.eslint.json'),
  ],
}

解決方法 3:tsconfigRootDir オプションを使用する

tsconfigRootDirオプションを使用すると、tsconfig.json の検索基準ディレクトリを明示的に指定できます。これはモノレポ構成で特に有効です。

javascript// .eslintrc.js
module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: {
    tsconfigRootDir: __dirname,
    project: ['./tsconfig.json'],
  },
  // その他の設定...
};

tsconfigRootDirを設定すると、projectで指定されたパスは、このディレクトリからの相対パスとして解釈されます。これにより、パス解決の曖昧さがなくなりますね。

解決方法 4:tsconfig.eslint.json を作成する

プロジェクトの構成が複雑な場合、ESLint 専用のtsconfig.eslint.jsonを作成すると管理が楽になります。

まず、プロジェクトルートにtsconfig.eslint.jsonを作成します。

json{
  "extends": "./tsconfig.json",
  "include": ["src/**/*", "tests/**/*", ".eslintrc.js"]
}

ここでは、元のtsconfig.jsonを継承しつつ、ESLint で解析したいファイルをincludeで明示的に指定しています。.eslintrc.js自体も TypeScript で型チェックしたい場合は含めると良いでしょう。

次に、ESLint 設定でこのファイルを参照します。

javascript// .eslintrc.js
module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: './tsconfig.eslint.json',
  },
  // その他の設定...
};

この方法のメリットは、ビルド用の設定と Lint 用の設定を分離できることです。ビルド時には不要なテストファイルなども、Lint 対象に含めることができますね。

解決方法 5:ignorePatterns 設定を活用する

ESLint で解析する必要のないファイルは、ignorePatternsで除外することで、tsconfig.json との不整合を防げます。

javascript// .eslintrc.js
module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: './tsconfig.json',
  },
  ignorePatterns: [
    'node_modules/',
    'dist/',
    'build/',
    '*.config.js',
  ],
  // その他の設定...
};

ignorePatternsに指定されたファイルは、ESLint の解析対象から除外されます。これにより、tsconfig.json のincludeexclude設定と矛盾することを防げます。

解決方法 6:createDefaultProgram を使用する(非推奨だが有効)

型情報を必要としないルールのみを使用している場合、createDefaultProgramオプションを有効にすることで、tsconfig.json が見つからない場合でもエラーを回避できます。

javascript// .eslintrc.js
module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: './tsconfig.json',
    createDefaultProgram: true, // 非推奨
  },
  // その他の設定...
};

ただし、このオプションは非推奨です。パフォーマンスが低下し、型情報を必要とするルールが正しく動作しない可能性があります。一時的な回避策としてのみ使用し、根本的な解決を目指しましょう。

具体例

ケース 1:Next.js プロジェクトでの設定

Next.js プロジェクトでは、複数の tsconfig.json ファイルが存在することが一般的です。以下は、典型的なディレクトリ構造です。

textmy-next-app/
├── .eslintrc.js
├── tsconfig.json
├── next.config.js
├── src/
│   └── app/
│       └── page.tsx
└── package.json

このような構造の場合、以下のように ESLint を設定します。

javascript// .eslintrc.js
const path = require('path');

module.exports = {
  extends: [
    'next/core-web-vitals',
    'plugin:@typescript-eslint/recommended',
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    tsconfigRootDir: __dirname,
    project: './tsconfig.json',
  },
  plugins: ['@typescript-eslint'],
  ignorePatterns: [
    'node_modules/',
    '.next/',
    'out/',
    'next.config.js',
  ],
};

以下は、Next.js プロジェクトでの ESLint 設定の構造を示した図です。

mermaidflowchart TD
  eslintrc[".eslintrc.js"] -->|参照| tsconfig["tsconfig.json"]
  eslintrc -->|除外設定| ignore["ignorePatterns"]
  tsconfig -->|include| src["src/**/*"]
  ignore -->|除外| next[".next/"]
  ignore -->|除外| modules["node_modules/"]
  src -->|解析対象| components["コンポーネント"]
  components -->|Lint実行| result["検証結果"]

重要なポイント

  • tsconfigRootDir: __dirnameでパス解決の基準を明確化
  • ignorePatternsでビルド成果物を除外
  • Next.js の推奨設定next​/​core-web-vitalsを継承

ケース 2:モノレポ(Yarn Workspaces)での設定

モノレポ構成では、各パッケージごとに ESLint 設定と tsconfig.json を管理するのが一般的です。

textmonorepo/
├── .eslintrc.js (ルート設定)
├── tsconfig.json (ルート設定)
├── packages/
│   ├── web/
│   │   ├── .eslintrc.js
│   │   ├── tsconfig.json
│   │   └── src/
│   └── api/
│       ├── .eslintrc.js
│       ├── tsconfig.json
│       └── src/
└── package.json

ルートの設定ファイルは、共通設定として機能させます。

javascript// monorepo/.eslintrc.js (ルート)
module.exports = {
  root: true, // ルート設定であることを明示
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint'],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
  ],
};

各パッケージでは、ルート設定を継承しつつ、独自の tsconfig.json を指定します。

javascript// packages/web/.eslintrc.js
const path = require('path');

module.exports = {
  parserOptions: {
    tsconfigRootDir: __dirname,
    project: './tsconfig.json',
  },
  // このパッケージ固有のルール
  rules: {
    '@typescript-eslint/no-unused-vars': 'error',
  },
};

同様に、apiパッケージでも設定します。

javascript// packages/api/.eslintrc.js
const path = require('path');

module.exports = {
  parserOptions: {
    tsconfigRootDir: __dirname,
    project: './tsconfig.json',
  },
  rules: {
    '@typescript-eslint/explicit-function-return-type':
      'warn',
  },
};

ポイント

  • ルートにroot: trueを設定し、設定の継承を制御
  • 各パッケージでtsconfigRootDir: __dirnameを使用
  • パッケージごとに異なる tsconfig.json を参照可能

ケース 3:テストファイルを含む設定

テストファイルは、本番コードとは異なる tsconfig 設定を使いたい場合があります。以下のような構造を考えてみましょう。

textproject/
├── .eslintrc.js
├── tsconfig.json
├── tsconfig.test.json
├── src/
│   └── index.ts
└── tests/
    └── index.test.ts

まず、tsconfig.test.jsonを作成します。

json{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "types": ["jest", "node"]
  },
  "include": ["tests/**/*", "src/**/*"]
}

次に、ESLint 設定で複数の tsconfig.json を指定します。

javascript// .eslintrc.js
const path = require('path');

module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: {
    tsconfigRootDir: __dirname,
    project: ['./tsconfig.json', './tsconfig.test.json'],
  },
  overrides: [
    {
      // テストファイル専用の設定
      files: ['tests/**/*.test.ts'],
      env: {
        jest: true,
      },
      rules: {
        '@typescript-eslint/no-explicit-any': 'off',
      },
    },
  ],
};

overridesを使用することで、テストファイルには異なるルールを適用できます。これにより、テストコードでの柔軟性を保ちつつ、本番コードの厳格性も維持できますね。

ケース 4:エディタ統合での問題解決

VS Code などのエディタでは、ESLint プラグインがバックグラウンドで動作します。この場合、ワークスペースのルートディレクトリが重要になります。

VS Code の設定ファイル(.vscode​/​settings.json)で、ESLint の作業ディレクトリを明示的に指定できます。

json{
  "eslint.workingDirectories": [
    {
      "mode": "auto"
    }
  ],
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ]
}

モノレポの場合は、各パッケージを個別に指定することもできます。

json{
  "eslint.workingDirectories": [
    "./packages/web",
    "./packages/api"
  ]
}

これにより、エディタ上での ESLint エラーも正しく表示されるようになります。

ケース 5:トラブルシューティング手順

エラーが解決しない場合は、以下の手順で原因を特定しましょう。

ステップ 1:パスの確認

ESLint 設定ファイルにログ出力を追加して、パスを確認します。

javascript// .eslintrc.js
const path = require('path');

const tsconfigPath = path.resolve(
  __dirname,
  './tsconfig.json'
);
console.log('tsconfig.json path:', tsconfigPath);
console.log(
  'File exists:',
  require('fs').existsSync(tsconfigPath)
);

module.exports = {
  // 設定...
};

ステップ 2:TSConfig の検証

tsconfig.json の構文が正しいか確認します。

bashyarn tsc --noEmit

構文エラーがある場合、このコマンドでエラーが表示されます。

ステップ 3:ESLint のキャッシュクリア

ESLint のキャッシュが原因で問題が発生している可能性があります。

bashyarn eslint --clear-cache

キャッシュをクリアしてから、再度 ESLint を実行してみましょう。

ステップ 4:依存関係の確認

必要なパッケージが正しくインストールされているか確認します。

bashyarn list --pattern "@typescript-eslint"

以下のパッケージが必要です。

json{
  "devDependencies": {
    "@typescript-eslint/parser": "^6.0.0",
    "@typescript-eslint/eslint-plugin": "^6.0.0",
    "eslint": "^8.0.0",
    "typescript": "^5.0.0"
  }
}

バージョンの互換性も重要です。@typescript-eslint​/​parser@typescript-eslint​/​eslint-pluginは、同じバージョンを使用しましょう。

まとめ

「Parsing error: Cannot read file 'tsconfig.json'」エラーは、ESLint が TypeScript の設定ファイルを正しく読み込めない場合に発生します。このエラーを解決するための重要なポイントをまとめます。

基本的な解決アプローチ .eslintrc.jsparserOptions.project設定を正しく指定することが最も重要です。相対パスの基準点を理解し、必要に応じてtsconfigRootDirオプションを使用しましょう。

環境に応じた設定 Next.js プロジェクト、モノレポ構成、テストファイルを含む構成など、プロジェクトの特性に応じて適切な設定方法を選択してください。複雑な構成の場合は、tsconfig.eslint.jsonを作成して管理を分離すると良いですね。

トラブルシューティング エラーが解決しない場合は、パスの確認、TSConfig の検証、キャッシュクリア、依存関係の確認を順番に実施してください。console.logでパスを出力することで、問題の原因を特定しやすくなります。

パフォーマンスとメンテナンス性 createDefaultProgramオプションは非推奨なので、根本的な解決を目指しましょう。また、ignorePatternsを適切に設定することで、不要なファイルの解析を避け、パフォーマンスを向上できます。

TypeScript と ESLint の組み合わせは、型安全性とコード品質の両方を高める強力なツールです。正しく設定することで、開発体験が大きく向上しますので、ぜひこの記事を参考に設定を見直してみてください。

関連リンク