<!-- llms-explorer concept facts · https://llms-explorer.com/tree/typescript-eslint-typed-linting/ · pack 2026-09-08 · ~6511 tokens -->

# TypeScript ESLint Typed Linting

> A lang-js-ts reference for linting TypeScript with typescript-eslint v8: stand up an

Parent: [TypeScript Expert](https://llms-explorer.com/tree/typescript-expert/) · 18 facets · 81 facts · page: https://llms-explorer.com/tree/typescript-eslint-typed-linting/

## typescript-eslint & Type-Aware Linting — flat config, `projectService`, typed rules

- A lang-js-ts reference for linting TypeScript with typescript-eslint v8: stand up an eslint.config.js flat config, turn on type-aware (typed) linting via parserOptions.projectService, pick the right shared config, and know which high-value rules need type information versus which are purely syntactic. The goal: a correct, version-appropriate ESLint setup the first time, with the typed-linting performance cost understood and scoped. Defer the TypeScript compiler API / parserServices internals, general bundler/linter choice, tsconfig strictness, and non-TS ESLint config to the siblings listed below. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#typescript-eslint-type-aware-linting-flat-config-projectservice-typed-rules)

## Overview

- typescript-eslint is the toolkit that lets ESLint understand TypeScript. Two packages do the work, both re-exported from the umbrella typescript-eslint package: — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#overview)
  - @typescript-eslint/parser - replaces ESLint's default (Espree) parser so ESLint can read TS syntax (types, generics, decorators) into an AST. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#overview)
  - @typescript-eslint/eslint-plugin - the rules themselves (~100+), including the typed rules. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#overview)
- Linting is not type-checking. ESLint + typescript-eslint finds bad practices and likely bugs (floating promises, unsafe any, dead conditions). It does not replace tsc: you still run tsc --noEmit as the type gate. The two are complementary - tsc proves the program type-checks; typed linting enforces opinions the compiler doesn't (e.g. "you ignored this promise"). — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#overview)
- Version anchor (memorize - these drive "is this available / how do I configure it" questions): — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#overview)
- > Flat config only. This reference uses eslint.config.js/.mjs. If you're on a legacy > .eslintrc, migrate first - ESLint 9 made flat config the default and v8 of typescript-eslint > documents it exclusively. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#overview)

## The two config helpers (and the one thing that breaks copy-paste)

- typescript-eslint ships shared configs as arrays of flat-config objects. How you splice them in depends on which helper you use, and the spread (...) is load-bearing: — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#the-two-config-helpers-and-the-one-thing-that-breaks-copy-paste)
  - tseslint.config(...) - typescript-eslint's own helper. Takes config objects as positional arguments, so array-valued configs must be spread: ...tseslint.configs.recommendedTypeChecked. Forgetting the spread passes an array where an object is expected → broken config. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#the-two-config-helpers-and-the-one-thing-that-breaks-copy-paste)
  - defineConfig(...) from eslint/config (ESLint 9.x) - the newer, framework-native helper. It flattens arrays for you, so you do not spread: pass tseslint.configs.recommendedTypeChecked directly, either positionally or inside an extends: [...] array. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#the-two-config-helpers-and-the-one-thing-that-breaks-copy-paste)
- Both are valid in v8. Lead with whichever your project already uses; the rules and parserOptions are identical between them. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#the-two-config-helpers-and-the-one-thing-that-breaks-copy-paste)

## Enabling type-aware (typed) linting

- "Typed linting" means rules can call into the TypeScript type checker (parserServices / getTypeChecker()) to reason about the types of expressions, not just their syntax. That's what makes no-floating-promises (is this expression a Promise?) possible at all. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#enabling-type-aware-typed-linting)
- To turn it on you (1) extend a *TypeChecked config and (2) tell the parser how to find type info via parserOptions.projectService: — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#enabling-type-aware-typed-linting)
- The same setup with the newer defineConfig helper (no spread; extends: arrays): — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#enabling-type-aware-typed-linting)
- tsconfigRootDir anchors relative tsconfig lookups to the config file's directory. Pair projectService: true with tsconfigRootDir: import.meta.dirname (ESM) or __dirname (CJS) - omit it and the parser resolves tsconfigs relative to the CWD, a real "works on my machine" footgun. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#enabling-type-aware-typed-linting)

## `projectService` vs `project` (and the `EXPERIMENTAL_` history)

- For files outside any tsconfig (root config files, scripts), projectService takes an options object instead of true: — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#projectservice-vs-project-and-the-experimental_-history)
- allowDefaultProject is a glob of out-of-project files to lint with type information - no extra tsconfig or compiler options needed. (Mechanics of parserServices/the checker API itself → typescript-compiler-api.) — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#projectservice-vs-project-and-the-experimental_-history)

## Shared configs (which require type info)

- Extend a preset rather than enabling rules one by one. The ***TypeChecked** variants require typed linting (projectService/project); the plain ones do not. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#shared-configs-which-require-type-info)
- Picking: no type info → recommended (+ stylistic). Type info → recommendedTypeChecked (+ stylisticTypeChecked). Reach for strict* only if a real share of the team is highly TS-proficient and will tolerate the friction. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#shared-configs-which-require-type-info)

## High-value typed rules (and the one syntactic exception)

- These are the rules that justify paying the typed-linting cost. All but the last require type information: — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#high-value-typed-rules-and-the-one-syntactic-exception)
- consistent-type-imports is the exception worth calling out: it's purely about import syntax, so it runs without type info. It pairs with TypeScript's verbatimModuleSyntax / isolatedModules to make type-only imports explicit and prevent a single-file transpiler from emitting a broken value import. (The tsconfig flags themselves → typescript-compiler-config.) — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#high-value-typed-rules-and-the-one-syntactic-exception)

## Turning off ESLint rules that conflict with TypeScript

- Several core ESLint rules are wrong or redundant under TypeScript - the compiler already covers them, or they false-positive on TS syntax. The classic is no-undef: TS already errors on undefined identifiers, and no-undef flags valid TS (global types, ambient declarations). You don't disable these by hand - typescript-eslint's recommended configs include eslintRecommended, which turns off the core rules TS subsumes (no-undef, no-dupe-class-members, no-redeclare, etc.). Likewise, prefer the typescript-eslint extension rules* (e.g. @typescript-eslint/no-unused-vars, @typescript-eslint/no-shadow) over the core versions, and disable the core one when you enable the TS variant. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#turning-off-eslint-rules-that-conflict-with-typescript)

## Tools / Frameworks

- typescript-eslint (umbrella package) - install eslint typescript typescript-eslint (plus @eslint/js for js.configs.recommended). Exposes tseslint.config, tseslint.parser, tseslint.plugin, and tseslint.configs.*. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#tools-frameworks)
- @eslint/js - provides ESLint's own js.configs.recommended base. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#tools-frameworks)
- defineConfig / eslint/config (ESLint 9) - the framework-native flat-config helper; the alternative to tseslint.config(). — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#tools-frameworks)
- tsgolint - a Go-based engine running typescript-eslint's typed rules natively, used as the backend for oxlint's type-aware preview. The fast-typed-linting frontier. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#tools-frameworks)
- Biome / oxlint - Rust-based all-in-one lint/format tools. Fast, but see Anti-Patterns for the typed-linting gap. Choosing between them and ESLint is out of scope → javascript-build-tooling-bundlers. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#tools-frameworks)

## Methodology

- Start from a preset, not hand-rolled rules. Extend recommended (no types) or recommendedTypeChecked (types) and only add/override specific rules afterward. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#methodology)
- Decide if you want typed linting. If yes, set parserOptions.projectService: true + tsconfigRootDir: import.meta.dirname and extend a *TypeChecked config. If you only want fast syntactic linting, stay on recommended and skip projectService entirely. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#methodology)
- Get the helper/spread right. tseslint.config(...) → spread array configs (...tseslint.configs.recommendedTypeChecked). defineConfig(...) → no spread. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#methodology)
- Scope out non-TS files. Add a { files: ['**/*.js'], extends: [tseslint.configs.disableTypeChecked] } block so plain JS / config files don't trip typed rules (or error for lacking a Program). — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#methodology)
- Enable the flagship typed rules deliberately if the preset doesn't already: at minimum no-floating-promises and no-misused-promises - they catch real production bugs. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#methodology)
- Keep tsc --noEmit in CI. Lint and type-check are separate gates; run both. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#methodology)
- Verify by running eslint . and confirming typed rules fire on a known floating promise. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#methodology)

## Practical Patterns

- Recommended typed setup (most TS projects), tseslint.config() form: — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#practical-patterns)
- Multi-plugin config (Jest on tests), typed linting off on JS - defineConfig form: — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#practical-patterns)
- Linting out-of-project files (root config files) without a dedicated tsconfig: — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#practical-patterns)
- Migrating off the old project option: — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#practical-patterns)

## Anti-Patterns

- Forgetting the spread under tseslint.config(). tseslint.configs.recommendedTypeChecked (no ...) passes an array where a config object is expected → silent misconfiguration or a crash. Spread it. (Under defineConfig, do the opposite: don't spread.) — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#anti-patterns)
- **Enabling *TypeChecked without projectService/project.** Typed rules need a Program; without one you get "parserOptions.project has been set … but … was not found" or the rules simply don't run. Set projectService: true. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#anti-patterns)
- Running typed rules over plain JS / config files. They error (no Program) or noise. disableTypeChecked on a **/*.js block. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#anti-patterns)
- Omitting tsconfigRootDir. Relative tsconfig resolution then depends on the CWD - flaky across editor vs CLI vs CI. Always set import.meta.dirname / __dirname. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#anti-patterns)
- Treating eslint as the type-checker. Linting ≠ tsc. Typed linting catches practices, not type errors; you still run tsc --noEmit. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#anti-patterns)
- Hand-disabling no-undef and friends. The recommended* configs already do this via eslintRecommended. Manually toggling core rules that conflict with TS just duplicates that. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#anti-patterns)
- Expecting Biome/oxlint to fully replace typed linting. As of 2025–26, Biome 2.0 added type inference (~85% of typescript-eslint's typed coverage) and oxlint added a tsgolint-backed type-aware preview - but the flagship typed rules (no-floating-promises, the no-unsafe- family) are exactly the guarantees a purely-syntactic Rust linter loses. If those rules matter, keep typescript-eslint. (Picking a toolchain overall → javascript-build-tooling-bundlers.)* — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#anti-patterns)
- Using the all config. Many rules conflict; it's not semver-stable. Extend a recommended or strict preset instead. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#anti-patterns)
- consistent-type-imports "needs type info." It doesn't - it's syntactic. Don't gate it behind projectService. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#anti-patterns)

## Troubleshooting

- "You have used a rule which requires type information, but … parserOptions … not set" → you extended a *TypeChecked config without projectService/project. Add projectService: true. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#troubleshooting)
- "… was not found by the project service. Consider … allowDefaultProject" → an out-of-project file (a root config) hit a typed rule. Add it to allowDefaultProject, or disableTypeChecked for that glob. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#troubleshooting)
- Lint is very slow / high memory → typed linting builds the TS program. Levers: prefer projectService over project; narrow files; disableTypeChecked on non-source globs; run typed lint as its own CI step; ensure tsconfig include isn't pulling in the world. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#troubleshooting)
- Rules don't fire / config seems ignored → flat config resolution. Confirm the file is eslint.config.js/.mjs, that you're on ESLint 9 (flat-config default), and (under tseslint.config) that array configs are spread. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#troubleshooting)
- no-undef flags valid TS (global types, ambient decls) → you re-enabled it manually; the recommended* configs disable it on purpose. Remove the override. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#troubleshooting)
- EXPERIMENTAL_useProjectService errors / deprecation → renamed to projectService in v8; update the key. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#troubleshooting)
- import type rule won't activate → that's consistent-type-imports, which is syntactic; it needs no projectService. Make sure it's actually enabled in rules, not assumed via a preset. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#troubleshooting)
- Editor and CLI disagree on types → divergent tsconfigs. projectService uses the editor's Project Service APIs, which helps; ensure both resolve the same tsconfig.json via tsconfigRootDir. — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#troubleshooting)

## References

- typescript-eslint - Getting Started (flat config quick start): https://typescript-eslint.io/getting-started/ — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#references)
- typescript-eslint - Typed Linting (projectService, recommendedTypeChecked): https://typescript-eslint.io/getting-started/typed-linting/ — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#references)
- typescript-eslint - Shared Configs (every preset, type-info matrix): https://typescript-eslint.io/users/configs/ — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#references)
- typescript-eslint - Rules (the 💭 "requires type information" marker): https://typescript-eslint.io/rules/ — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#references)
- typescript-eslint - @typescript-eslint/parser (projectService, project, tsconfigRootDir): https://typescript-eslint.io/packages/parser/ — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#references)
- typescript-eslint - "Typed Linting with Project Service" blog: https://typescript-eslint.io/blog/project-service/ — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#references)
- typescript-eslint - Announcing v8 (EXPERIMENTAL_useProjectService → projectService): https://typescript-eslint.io/blog/announcing-typescript-eslint-v8/ — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#references)
- ESLint - Configuration Files (flat config, defineConfig): https://eslint.org/docs/latest/use/configure/configuration-files — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#references)
- Biome 2.0 - type inference / type-aware rules: https://biomejs.dev/blog/biome-v2-0-0/ — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#references)
- oxc - Oxlint Type-Aware Preview (tsgolint): https://oxc.rs/blog/2025-08-17-oxlint-type-aware — [source](https://llms-explorer.com/sources/mdb-context-hub/typescript-eslint-typed-linting/#references)

## Where this helps

- Setting up type-aware ESLint rules (like no-floating-promises) on a TypeScript project that only has syntactic linting today. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Debugging why a typed rule fires the "parserOptions ... was not found" error on a root config file that sits outside any tsconfig. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Deciding whether to migrate from ESLint's legacy .eslintrc to flat config, since typescript-eslint v8 documents flat config exclusively. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Diagnosing why linting has become extremely slow or memory-hungry after enabling a *TypeChecked preset. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Project ideas

- Set up a flat eslint.config.js that extends tseslint.configs.recommendedTypeChecked with parserOptions.projectService: true and tsconfigRootDir set, then confirm no-floating-promises actually fires on a known bug. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Build a config that scopes typed linting off of non-TS files (plain JS, config files) using a disableTypeChecked override block, so out-of-project files don't error for lacking a Program. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Wire tsc --noEmit and eslint . as two separate, required CI gates, since typed linting catches practices while tsc catches actual type errors. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Benchmark lint runtime and memory with and without projectService enabled on a large repo, to quantify the real cost of typed linting before rolling it out broadly. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Common mistakes

- Forgetting to spread an array-valued shared config under tseslint.config() (...tseslint.configs.recommendedTypeChecked) - passing the array directly causes a silent misconfiguration. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Enabling a *TypeChecked preset without setting parserOptions.projectService (or project) - typed rules need a Program and either fail or silently don't run without one. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Running typed rules over plain JS or config files that have no Program behind them, instead of scoping them out with a disableTypeChecked override. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Omitting tsconfigRootDir - relative tsconfig resolution then depends on the current working directory, producing flaky results between editor, CLI, and CI. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Known issues

- Typed linting is meaningfully slower and more memory-hungry than syntactic linting because it builds a real TypeScript program behind the scenes - this cost is unavoidable, only manageable by narrowing scope or running it as its own CI step. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- consistent-type-imports is the one high-value rule that's purely syntactic and needs no type information - gating it behind projectService wastes the one rule that didn't need the cost. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Biome 2.0 and oxlint's tsgolint-backed preview have started closing the gap on type-aware linting (Biome reports roughly 85% of typescript-eslint's typed coverage as of 2025-26), but neither is a full replacement yet. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Typed linting is not a substitute for tsc --noEmit - it catches suspicious patterns like floating promises and unsafe any, but does not replace the compiler's actual type-correctness checking. — [source](https://llms-explorer.com/tree/typescript-eslint-typed-linting/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Context files

- [TypeScript ESLint Typed Linting](https://llms-explorer.com/downloads/sources/mdb-context-hub/typescript-eslint-typed-linting.md)
