ESLint 14 🛡️ TypeScript Integration — typescript-eslint parser and rules
TypeScript is not JavaScript, and ESLint’s core parser cannot understand its syntax. The typescript-eslint project provides the parser that translates TypeScript’s syntax into an ESTree-compatible AST, and the rules that use TypeScript’s type information to catch bugs that a syntax-only linter cannot detect. The project is a monorepo that includes the parser, the plugin, and a set of shared configurations. The installation is a single package (typescript-eslint) that bundles the parser, the plugin, and the configs together.
This chapter covers the parser setup, the shared configurations that most projects start with, the type-aware rules that require the parser to read the TypeScript compiler’s type information, and the projectService option that simplifies the type-aware configuration.
Key point: The parser alone gives you TypeScript syntax support. The type-aware rules require the parser to also read the tsconfig.json and build a TypeScript program. The parserOptions.projectService: true option is the modern way to enable this, and it replaces the older parserOptions.project setting.
Why TypeScript needs its own parser and rules
The syntax problem. TypeScript adds syntax that JavaScript does not have: type annotations, interfaces, generics, enums, and decorators. The default ESLint parser (espree) fails on the first type annotation. The @typescript-eslint/parser wraps the TypeScript compiler’s parser and converts the TypeScript AST into the ESTree format that ESLint’s rules expect.
The type-aware problem. Some of the most valuable lint rules require type information. A rule like no-floating-promises needs to know whether an expression returns a Promise. A rule like no-unsafe-assignment needs to know whether a value has the type any. These rules cannot be implemented with syntax alone; they need the TypeScript compiler’s type checker.
The configuration problem. The parser, the plugin, and the configs are separate concerns. The typescript-eslint package bundles them into a single dependency and exports a configs object that applies the parser, the plugin, and the recommended rules in one step.
a. The installation and the basic configuration
The installation adds eslint, @eslint/js, typescript, and typescript-eslint as dev dependencies.
npm install --save-dev eslint @eslint/js typescript typescript-eslint
The eslint.config.mjs file imports the js config and the tseslint config, and applies them with the defineConfig helper.
// eslint.config.mjs
import js from '@eslint/js';
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
export default defineConfig({
files: ['**/*.{js,ts}'],
extends: [js.configs.recommended, tseslint.configs.recommended],
});
The files key scopes the config to JavaScript and TypeScript files. The extends array applies the ESLint recommended config first, then the typescript-eslint recommended config. The tseslint.configs.recommended object includes the parser registration, the plugin registration, and the rules. The project does not need to import the parser separately when it uses the config.
For a project that needs to lint all the standard extensions, the files pattern is extended:
files: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
The tseslint.configs.recommended config also disables the core ESLint rules that conflict with the TypeScript-specific rules, such as no-unused-vars and no-redeclare, and replaces them with the @typescript-eslint versions.
b. The shared configurations
The typescript-eslint project exports a tiered set of configs. The recommended config is the starting point. The strict config is a superset that adds more opinionated rules. The stylistic config adds rules about code patterns that do not affect program logic. The recommendedTypeChecked config adds rules that require type information, and the strictTypeChecked config combines the strict rules with the type-checked rules.
// eslint.config.mjs
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
...tseslint.configs.strict,
...tseslint.configs.stylistic,
);
The tseslint.config helper is an alternative to defineConfig that flattens the configs and applies them in order. The spread operator is used for the configs that are arrays.
The recommendedTypeChecked config is the one that requires the parser to be configured with type information. Without the projectService or project setting, the type-aware rules fail because the parser cannot determine the types of the expressions it is linting.
c. The projectService and type-aware linting
The parserOptions.projectService: true option is the modern mechanism for enabling type-aware linting. It uses the same TypeScript Project Service APIs that editors like VS Code use to build the type information.
// eslint.config.mjs
import tseslint from 'typescript-eslint';
export default tseslint.config(
...tseslint.configs.recommendedTypeChecked,
{
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
},
);
The projectService option automatically finds the nearest tsconfig.json for each file. It also allows type information to be computed for JavaScript files without the allowJs compiler option. The tsconfigRootDir option sets the base directory for the resolution.
The projectService option replaces the older parserOptions.project setting. The project option required the project to list the tsconfig.json paths explicitly, and it often required a separate tsconfig.eslint.json for files that were not included in the main config. The projectService option removes that requirement for most projects.
The allowDefaultProject option handles the case where a file is not included in the tsconfig.json, such as a root-level eslint.config.js or a vitest.config.ts. The option takes a glob array and allows those files to be linted with the default project compiler options.
parserOptions: {
projectService: {
allowDefaultProject: ['*.js', '*.ts'],
},
tsconfigRootDir: import.meta.dirname,
},
The projectService option is not compatible with the project option. Enabling both causes an error, and the recommendation is to use projectService and remove the project setting.
Complete Example Session
// ============================================
// PART 1: THE INSTALLATION
// ============================================
// npm install --save-dev eslint @eslint/js typescript typescript-eslint
// ============================================
// PART 2: THE BASIC CONFIG
// ============================================
import js from '@eslint/js';
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
export default defineConfig({
files: ['**/*.{js,ts}'],
extends: [js.configs.recommended, tseslint.configs.recommended],
});
// ============================================
// PART 3: THE EXTENDED FILES PATTERN
// ============================================
files: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
// ============================================
// PART 4: THE STRICT CONFIG
// ============================================
export default defineConfig({
extends: [
js.configs.recommended,
tseslint.configs.recommended,
tseslint.configs.strict,
],
});
// ============================================
// PART 5: THE STYLISTIC CONFIG
// ============================================
export default defineConfig({
extends: [
tseslint.configs.recommended,
tseslint.configs.stylistic,
],
});
// ============================================
// PART 6: THE TYPE-CHECKED CONFIG
// ============================================
export default tseslint.config(
...tseslint.configs.recommendedTypeChecked,
{
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
},
);
// ============================================
// PART 7: THE ALLOW DEFAULT PROJECT
// ============================================
parserOptions: {
projectService: {
allowDefaultProject: ['*.js'],
},
tsconfigRootDir: import.meta.dirname,
},
// ============================================
// PART 8: THE TSCONFIG ROOT DIR
// ============================================
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
// ============================================
// PART 9: THE CUSTOM RULE
// ============================================
rules: {
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/no-explicit-any': 'warn',
},
// ============================================
// PART 10: THE RUN
// ============================================
// npx eslint .
The ten parts covered the installation, the basic config, the extended files pattern, the strict config, the stylistic config, the type-checked config, the allowDefaultProject option, the tsconfigRootDir, the custom rules, and the run.
Quick Reference
The packages
| Package | Purpose |
|---|---|
typescript-eslint | Bundles parser, plugin, and configs |
typescript | The TypeScript compiler |
eslint | The linter |
@eslint/js | The core recommended config |
The configs
| Config | Type information | Strictness |
|---|---|---|
recommended | No | Base |
recommendedTypeChecked | Yes | Base + typed |
strict | No | Opinionated |
strictTypeChecked | Yes | Opinionated + typed |
stylistic | No | Code patterns |
stylisticTypeChecked | Yes | Code patterns + typed |
The parser options
| Option | Purpose |
|---|---|
projectService: true | Auto-detect tsconfig.json per file |
projectService.allowDefaultProject | Files outside the tsconfig |
tsconfigRootDir | Base directory for resolution |
project | Legacy setting, replaced by projectService |
The file extensions
| Pattern | Files |
|---|---|
**/*.{js,ts} | Basic |
**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx} | All standard extensions |
Best Practices
✅ Do This:
// Use the recommended config as the base
extends: [js.configs.recommended, tseslint.configs.recommended] // ✅
// Use projectService for type-aware linting
parserOptions: { projectService: true } // ✅
// Set tsconfigRootDir for predictable resolution
tsconfigRootDir: import.meta.dirname // ✅
// Use allowDefaultProject for root-level config files
projectService: { allowDefaultProject: ['*.js'] } // ✅
❌ Don’t Do This:
// Use the parser alone without the plugin
// The rules are not available // ❌
// Enable both project and projectService
parserOptions: { project: true, projectService: true } // ❌ (error)
// Use wide globs in the project option
project: ['./**/tsconfig.json'] // ❌ (slow)
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Type-aware rules fail | No projectService or project | Add projectService: true |
project and projectService error | Both settings enabled | Remove project |
| File is not in the tsconfig | allowDefaultProject not set | Add the glob |
| Parser is not applied | Config not extended | Use tseslint.configs.recommended |
| Core rule conflicts | Core rule not disabled | Use the recommended config |
Real-World Examples
1. The install
npm install --save-dev eslint @eslint/js typescript typescript-eslint
2. The basic config
extends: [js.configs.recommended, tseslint.configs.recommended]
3. The strict config
extends: [tseslint.configs.recommended, tseslint.configs.strict]
4. The type-checked config
extends: [tseslint.configs.recommendedTypeChecked]
5. The projectService
parserOptions: { projectService: true }
6. The allowDefaultProject
projectService: { allowDefaultProject: ['*.js'] }
7. The tsconfigRootDir
tsconfigRootDir: import.meta.dirname
8. The custom rule
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }]
9. The run
npx eslint .
10. The decision
Syntax only → recommended; Type-aware → recommendedTypeChecked + projectService
Visual
The parser and the config
┌─────────────────────────────────────────────────────────────┐
│ tseslint.configs.recommended │
│ ├── parser: @typescript-eslint/parser │
│ ├── plugin: @typescript-eslint │
│ └── rules: recommended │
│ │
│ The config applies the parser and the plugin. │
└─────────────────────────────────────────────────────────────┘
The type-aware flow
┌─────────────────────────────────────────────────────────────┐
│ TypeScript file │
│ │ │
│ ▼ │
│ @typescript-eslint/parser │
│ ├── parses the syntax │
│ └── reads the tsconfig (projectService) │
│ │ │
│ ▼ │
│ TypeScript program │
│ │ │
│ ▼ │
│ Type-aware rules (no-floating-promises, etc.) │
└─────────────────────────────────────────────────────────────┘
The config tiers
┌─────────────────────────────────────────────────────────────┐
│ recommended │
│ └── recommendedTypeChecked (adds type-aware rules) │
│ └── strict (adds opinionated rules) │
│ └── strictTypeChecked (adds typed strict) │
│ │
│ stylistic (adds pattern rules, independent) │
└─────────────────────────────────────────────────────────────┘
The projectService
┌─────────────────────────────────────────────────────────────┐
│ parserOptions: { projectService: true } │
│ │ │
│ ▼ │
│ Finds the nearest tsconfig.json per file │
│ │ │
│ ▼ │
│ Builds the type information │
│ │ │
│ ▼ │
│ Type-aware rules can run │
└─────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Package | typescript-eslint |
| Parser | @typescript-eslint/parser |
| Basic config | tseslint.configs.recommended |
| Strict config | tseslint.configs.strict |
| Type-checked config | tseslint.configs.recommendedTypeChecked |
| Type information | parserOptions.projectService: true |
| Legacy type info | parserOptions.project (replaced) |
| Root directory | tsconfigRootDir |
| Out-of-project files | allowDefaultProject |
Key takeaways:
typescript-eslintbundles the parser, the plugin, and the configs. The installation is a single package, and therecommendedconfig applies the parser and the plugin in one step. The project does not need to import the parser separately.- The
recommendedconfig is the starting point. It disables the core ESLint rules that conflict with the TypeScript rules and enables the TypeScript-specific versions. Thestrictandstylisticconfigs add more rules on top. - Type-aware rules require type information. The
recommendedTypeCheckedconfig enables rules that need the TypeScript compiler’s type checker. Without theprojectServiceorprojectsetting, those rules fail because the parser cannot determine the types. projectService: trueis the modern configuration. It finds the nearesttsconfig.jsonfor each file and uses the same TypeScript APIs that editors use. It replaces the olderprojectoption, which required explicit paths and often required a separatetsconfig.eslint.json.- The
allowDefaultProjectoption handles out-of-project files. A root-level config file that is not included in thetsconfig.jsoncan be linted with the default project compiler options. The option takes a glob array. - The
projectandprojectServiceoptions are mutually exclusive. Enabling both causes an error. The recommendation is to useprojectServiceand remove theprojectsetting.
Remember: The TypeScript integration is a parser and a set of rules. The parser translates the TypeScript syntax, and the config applies the parser, the plugin, and the rules. The type-aware rules need the parser to read the tsconfig.json, and the projectService option is the modern way to enable that. The recommended config is the starting point, the strict config adds opinionated rules, and the stylistic config adds pattern rules. The projectService is the key to the type-aware linting, and the allowDefaultProject is the escape hatch for the files that are not in the tsconfig.json.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!