| | |

ESLint 14 🛡️ TypeScript Integration — typescript-eslint parser and rules

⏱️ Estimated read: 9 min | |

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

PackagePurpose
typescript-eslintBundles parser, plugin, and configs
typescriptThe TypeScript compiler
eslintThe linter
@eslint/jsThe core recommended config

The configs

ConfigType informationStrictness
recommendedNoBase
recommendedTypeCheckedYesBase + typed
strictNoOpinionated
strictTypeCheckedYesOpinionated + typed
stylisticNoCode patterns
stylisticTypeCheckedYesCode patterns + typed

The parser options

OptionPurpose
projectService: trueAuto-detect tsconfig.json per file
projectService.allowDefaultProjectFiles outside the tsconfig
tsconfigRootDirBase directory for resolution
projectLegacy setting, replaced by projectService

The file extensions

PatternFiles
**/*.{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

PitfallWhy It HappensFix
Type-aware rules failNo projectService or projectAdd projectService: true
project and projectService errorBoth settings enabledRemove project
File is not in the tsconfigallowDefaultProject not setAdd the glob
Parser is not appliedConfig not extendedUse tseslint.configs.recommended
Core rule conflictsCore rule not disabledUse 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

ItemValue
Packagetypescript-eslint
Parser@typescript-eslint/parser
Basic configtseslint.configs.recommended
Strict configtseslint.configs.strict
Type-checked configtseslint.configs.recommendedTypeChecked
Type informationparserOptions.projectService: true
Legacy type infoparserOptions.project (replaced)
Root directorytsconfigRootDir
Out-of-project filesallowDefaultProject

Key takeaways:

  • typescript-eslint bundles the parser, the plugin, and the configs. The installation is a single package, and the recommended config applies the parser and the plugin in one step. The project does not need to import the parser separately.
  • The recommended config is the starting point. It disables the core ESLint rules that conflict with the TypeScript rules and enables the TypeScript-specific versions. The strict and stylistic configs add more rules on top.
  • Type-aware rules require type information. The recommendedTypeChecked config enables rules that need the TypeScript compiler’s type checker. Without the projectService or project setting, those rules fail because the parser cannot determine the types.
  • projectService: true is the modern configuration. It finds the nearest tsconfig.json for each file and uses the same TypeScript APIs that editors use. It replaces the older project option, which required explicit paths and often required a separate tsconfig.eslint.json.
  • The allowDefaultProject option handles out-of-project files. A root-level config file that is not included in the tsconfig.json can be linted with the default project compiler options. The option takes a glob array.
  • The project and projectService options are mutually exclusive. Enabling both causes an error. The recommendation is to use projectService and remove the project setting.

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.

Was this helpful?


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!