Question

How to Fix "parserOptions.project" Errors in ESLint with TypeScript

typescripteslinttypescript-eslint

Question

I created a new React Native project using the TypeScript template. After that, I deleted the template directory that came with the boilerplate and added ESLint with this configuration:

module.exports = {
  parser: "@typescript-eslint/parser",
  plugins: ["@typescript-eslint"],
  extends: ["airbnb-typescript-prettier"]
};

However, when I open babel.config.js, I get this error:

Parsing error: "parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: /Users/Dan/site/babel.config.js.
The file must be included in at least one of the projects provided.

Why does this happen, and how can I configure ESLint correctly so that JavaScript config files like babel.config.js do not cause this error?

Short Answer

By the end of this page, you will understand why ESLint sometimes throws a parserOptions.project error in TypeScript projects, especially for files like babel.config.js that are not part of tsconfig.json. You will learn what typed linting is, why ESLint checks TypeScript project boundaries, and the common ways to fix the problem in real projects.

Concept

When ESLint uses @typescript-eslint/parser with type-aware linting, it does more than read syntax. It also loads your TypeScript project configuration from tsconfig.json so it can understand types.

That is what parserOptions.project is about.

If ESLint is told to use a TypeScript project, then every file being linted must belong to that project. A file such as babel.config.js is usually a plain JavaScript config file, and it is often not included in tsconfig.json. When ESLint tries to lint it using the TypeScript parser in project mode, it fails because TypeScript does not consider that file part of the project.

This usually happens indirectly. In your config, airbnb-typescript-prettier commonly brings in settings that expect TypeScript project information. So even if you did not explicitly write parserOptions.project, the extended config may effectively require it.

Why this matters:

  • Type-aware linting enables more powerful rules.
  • But it also requires correct project boundaries.
  • Mixed codebases often contain:
    • TypeScript source files
    • JavaScript config files
    • build scripts
    • test setup files
  • These files should not always be linted the same way.

The fix is usually one of these:

  • Ignore config files that should not be linted with the TypeScript project.
  • Use ESLint overrides so .js config files use the default parser or a non-type-aware setup.
  • Create a dedicated tsconfig.eslint.json that includes the files ESLint should understand.

The key idea is simple: if ESLint uses TypeScript project mode, every linted file must be included in a matching TypeScript config, or it must be excluded or handled differently.

Mental Model

Think of tsconfig.json as a guest list for a private event.

  • TypeScript files included in tsconfig.json are on the list.
  • ESLint with parserOptions.project asks TypeScript to verify each guest.
  • babel.config.js arrives at the door.
  • TypeScript checks the guest list and says: "This file is not invited."
  • ESLint then shows the parsing error.

So the problem is not that babel.config.js is invalid JavaScript. The problem is that ESLint is asking TypeScript to treat it like a file from the TypeScript project, but it is not on the project's list.

Take Quiz

Syntax and Examples

A common TypeScript ESLint setup looks like this:

module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: "./tsconfig.json"
  },
  plugins: ["@typescript-eslint"],
  extends: [
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking"
  ]
};

This enables rules that require TypeScript type information.

If ESLint also tries to lint babel.config.js, you may get an error because that file is not included in tsconfig.json.

Option 1: Ignore config files

module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: "./tsconfig.json"
  },
  plugins: ["@typescript-eslint"],
  extends: ["airbnb-typescript-prettier"],
  ignorePatterns: ["babel.config.js", "metro.config.js"]
};

Use this when you do not need ESLint to check those files.

Option 2: Use overrides for JavaScript config files

module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: "./tsconfig.json"
  },
  plugins: ["@typescript-eslint"],
  extends: ["airbnb-typescript-prettier"],
  overrides: [
    {
      files: ["*.js", "*.config.js"],
      parserOptions: {
        project: null
      }
    }
  ]
};

This tells ESLint not to use TypeScript project mode for matching JavaScript files.

Option 3: Create tsconfig.eslint.json

{
  "extends": "./tsconfig.json",
  "include": [
    "src/**/*",
    "App.tsx",
    "babel.config.js"
  ]
}

Then point ESLint to that file:

module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: "./tsconfig.eslint.json"
  },
  plugins: ["@typescript-eslint"],
  extends: ["airbnb-typescript-prettier"]
};

This works when you intentionally want ESLint to see additional files.

Which option should beginners choose?

Usually:

  • Ignore config files if you do not care about linting them.
  • Use overrides if you want to lint them, but not with TypeScript project rules.
  • Use tsconfig.eslint.json if you want one clean linting setup across the whole repo.

Step by Step Execution

Consider this ESLint config:

module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: "./tsconfig.json"
  }
};

And assume tsconfig.json includes only TypeScript source files:

{
  "include": ["src/**/*", "App.tsx"]
}

Now ESLint opens babel.config.js.

What happens step by step

  1. ESLint starts linting babel.config.js.
  2. It sees that the parser is @typescript-eslint/parser.
  3. Because parserOptions.project is set, the parser loads tsconfig.json.
  4. TypeScript checks whether babel.config.js is part of the project.
  5. It is not listed in include, and it is not a TypeScript source file in the project.
  6. The parser cannot build type information for that file.
  7. ESLint reports:
The file must be included in at least one of the projects provided.

If you add an override

module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: "./tsconfig.json"
  },
  overrides: [
    {
      files: ["babel.config.js"],
      parserOptions: {
        project: null
      }
    }
  ]
};

Then the flow changes:

  1. ESLint starts linting babel.config.js.
  2. The override matches that file.
  3. For that file, project is disabled.
  4. ESLint parses it without trying to connect it to the TypeScript project.
  5. The error disappears.

Real World Use Cases

This issue appears often in projects that mix TypeScript application code with JavaScript tooling files.

Common examples:

  • React Native apps with babel.config.js and metro.config.js
  • React apps with vite.config.js or webpack.config.js
  • Node.js projects with jest.config.js or eslint.config.js
  • Monorepos with multiple tsconfig.json files
  • Build scripts stored in scripts/*.js

Typical situations:

  • You want strict TypeScript linting for src/**/*.ts and src/**/*.tsx.
  • You still want ESLint on config files.
  • But those config files should not require TypeScript project type information.

This separation is very normal in production codebases. Most real projects do not treat all files the same.

Take Quiz

Real Codebase Usage

In real projects, developers usually handle this with one of a few patterns.

Pattern 1: Type-aware linting only for TypeScript files

This is the most common setup.

module.exports = {
  root: true,
  overrides: [
    {
      files: ["*.ts", "*.tsx"],
      parser: "@typescript-eslint/parser",
      parserOptions: {
        project: "./tsconfig.json"
      },
      extends: [
        "plugin:@typescript-eslint/recommended",
        "plugin:@typescript-eslint/recommended-requiring-type-checking"
      ]
    },
    {
      files: ["*.js"],
      extends: ["eslint:recommended"]
    }
  ]
};

Why teams use it:

  • Clear separation between TS and JS files
  • Faster linting
  • Fewer parser errors

Pattern 2: Dedicated ESLint TypeScript config

Many teams create tsconfig.eslint.json so linting can include test files, scripts, or other files not used by the compiler.

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

This avoids changing the main compiler config just for ESLint.

Pattern 3: Ignore purely tool-related files

ignorePatterns: [
  "babel.config.js",
  "metro.config.js",
  "coverage/",
  "dist/"
]

This is common when those files are stable and rarely edited.

Pattern 4: Guarding lint scope in scripts

Teams often lint only application folders:

{
  "scripts": {
    "lint": "eslint src App.tsx"
  }
}

That prevents accidental linting of unsupported files.

The broader lesson is that ESLint configuration is often file-specific, not one-size-fits-all.

Common Mistakes

1. Using the TypeScript parser for every file

Broken example:

module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: "./tsconfig.json"
  }
};

Why it breaks:

  • ESLint applies the TypeScript project parser to all files, including .js config files.

How to avoid it:

  • Use overrides
  • Or ignore non-TypeScript files

2. Assuming extends does not affect parser behavior

Many configs such as Airbnb TypeScript presets bring in rules that need type information.

How to avoid it:

  • Read the preset documentation
  • Check whether it requires parserOptions.project

3. Adding JavaScript files to tsconfig.json without understanding the effect

Possible fix, but not always ideal:

{
  "include": ["src/**/*", "babel.config.js"]
}

Why this can be awkward:

  • Your compiler config is now mixing app code and tooling files
  • It may not be what you want for builds

Better option:

  • Use tsconfig.eslint.json

4. Forgetting that ESLint may lint the whole project

If your editor or CLI runs ESLint on every file, unsupported files will trigger errors.

How to avoid it:

  • Restrict lint paths
  • Use ignorePatterns
  • Add overrides

5. Setting project incorrectly

Broken example:

parserOptions: {
  project: "tsconfig.eslint.json"
}

If that file does not exist or does not include the expected files, you will still get errors.

How to avoid it:

  • Verify the path
  • Verify include patterns carefully

6. Expecting JavaScript config files to behave like TypeScript source files

babel.config.js is usually just a Node configuration file. It does not need the same TypeScript linting rules as App.tsx.

How to avoid it:

  • Treat config files separately

Comparisons

ApproachWhat it doesBest forProsCons
ignorePatternsSkips specific files entirelyConfig files you do not care to lintSimpleNo linting on those files
overridesUses different rules/parser settings per file typeMixed TS and JS projectsFlexible and cleanSlightly more config
tsconfig.eslint.jsonCreates a special TS project for lintingProjects needing typed linting across more filesKeeps compiler config separateExtra file to maintain
Add files to tsconfig.jsonExpands the main TS projectSmall projectsQuick fixCan blur build and lint concerns

tsconfig.json vs tsconfig.eslint.json

FileMain purpose
tsconfig.jsonControls TypeScript compilation
tsconfig.eslint.jsonControls which files ESLint can use for type-aware linting

Linting with type info vs without type info

ModeUses parserOptions.projectStronger rulesMore setup
Without type infoNoNoEasier
With type infoYesYesMore strict about included files
Take Quiz

Cheat Sheet

// Type-aware TypeScript ESLint setup
module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: "./tsconfig.json"
  }
};

Key rule

If parserOptions.project is used, every linted file must:

  • be included in the referenced TypeScript config
  • or be ignored
  • or be handled by an override without project

Common fixes

// Ignore file
ignorePatterns: ["babel.config.js"]
// Disable project mode for specific files
overrides: [
  {
    files: ["*.js", "*.config.js"],
    parserOptions: {
      project: null
    }
  }
]
// Use a dedicated ESLint TS config
parserOptions: {
  project: "./tsconfig.eslint.json"
}

Good default strategy

  • TypeScript app code: type-aware linting
  • JavaScript config files: normal linting or ignore them

Typical files that trigger this issue

  • babel.config.js
  • metro.config.js
  • jest.config.js
  • webpack.config.js
  • vite.config.js

Reminder

extends: ["airbnb-typescript-prettier"] may require TypeScript-aware setup, even if you did not explicitly add all parser options yourself.

FAQ

Why does ESLint complain about babel.config.js in a TypeScript project?

Because ESLint is using the TypeScript parser in project mode, and babel.config.js is usually not included in tsconfig.json.

What does parserOptions.project do?

It tells @typescript-eslint/parser to load a TypeScript project so ESLint can run rules that require type information.

Should I add babel.config.js to tsconfig.json?

Usually not. It is often better to ignore it, use an override, or create tsconfig.eslint.json.

What is the best fix for most projects?

Use overrides so TypeScript files use project-based linting and JavaScript config files do not.

Can I lint JavaScript and TypeScript files in the same project?

Yes. This is very common. You usually configure different parser options or rule sets for different file patterns.

Why did this start after extending an Airbnb TypeScript config?

Because TypeScript-focused shared configs often expect type-aware linting and may rely on parserOptions.project.

What is tsconfig.eslint.json used for?

It is a separate TypeScript config file used only for linting, so you can include files needed by ESLint without changing your build config.

Take Quiz

Mini Project

Description

Create an ESLint setup for a small React Native TypeScript project that includes both application code and JavaScript config files. The goal is to avoid parserOptions.project errors while keeping strong linting for TypeScript files.

Goal

Configure ESLint so .ts and .tsx files use type-aware linting, while babel.config.js is linted safely without causing project inclusion errors.

Requirements

  • Create an ESLint configuration that uses @typescript-eslint/parser for TypeScript files.
  • Apply parserOptions.project only where type-aware linting is needed.
  • Ensure babel.config.js does not trigger the project config parsing error.
  • Keep the setup practical for a React Native codebase.
Take Quiz

Keep learning

Related questions

View more