Question
How to Fix "parserOptions.project" Errors in ESLint with TypeScript
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
.jsconfig files use the default parser or a non-type-aware setup. - Create a dedicated
tsconfig.eslint.jsonthat 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.jsonare on the list. - ESLint with
parserOptions.projectasks TypeScript to verify each guest. babel.config.jsarrives 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.
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.jsonif 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
- ESLint starts linting
babel.config.js. - It sees that the parser is
@typescript-eslint/parser. - Because
parserOptions.projectis set, the parser loadstsconfig.json. - TypeScript checks whether
babel.config.jsis part of the project. - It is not listed in
include, and it is not a TypeScript source file in the project. - The parser cannot build type information for that file.
- 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:
- ESLint starts linting
babel.config.js. - The override matches that file.
- For that file,
projectis disabled. - ESLint parses it without trying to connect it to the TypeScript project.
- 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.jsandmetro.config.js - React apps with
vite.config.jsorwebpack.config.js - Node.js projects with
jest.config.jsoreslint.config.js - Monorepos with multiple
tsconfig.jsonfiles - Build scripts stored in
scripts/*.js
Typical situations:
- You want strict TypeScript linting for
src/**/*.tsandsrc/**/*.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.
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
.jsconfig 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
includepatterns 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
| Approach | What it does | Best for | Pros | Cons |
|---|---|---|---|---|
ignorePatterns | Skips specific files entirely | Config files you do not care to lint | Simple | No linting on those files |
overrides | Uses different rules/parser settings per file type | Mixed TS and JS projects | Flexible and clean | Slightly more config |
tsconfig.eslint.json | Creates a special TS project for linting | Projects needing typed linting across more files | Keeps compiler config separate | Extra file to maintain |
Add files to tsconfig.json | Expands the main TS project | Small projects | Quick fix | Can blur build and lint concerns |
tsconfig.json vs tsconfig.eslint.json
| File | Main purpose |
|---|---|
tsconfig.json | Controls TypeScript compilation |
tsconfig.eslint.json | Controls which files ESLint can use for type-aware linting |
Linting with type info vs without type info
| Mode | Uses parserOptions.project | Stronger rules | More setup |
|---|---|---|---|
| Without type info | No | No | Easier |
| With type info | Yes | Yes | More strict about included files |
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.jsmetro.config.jsjest.config.jswebpack.config.jsvite.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.
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/parserfor TypeScript files. - Apply
parserOptions.projectonly where type-aware linting is needed. - Ensure
babel.config.jsdoes not trigger the project config parsing error. - Keep the setup practical for a React Native codebase.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.