Quick take: On 1 October 2026 the newest typescript-eslint is 8.71.0 and its peer range still stops below TypeScript 6.1.0.
npm install typescript@7.0.2 typescript-eslintfails with ERESOLVE, and forcing it just breaks ESLint on startup. Alias the TypeScript 6 API astypescriptand add the native compiler as@typescript/native, and both tools run.
For most projects this is the real blocker on a TypeScript 7 migration, because the lint step stops existing. It isn't a type error. It isn't a rule change. It's a package that won't sit in the same tree as the compiler.
What does the failure look like?
Start from an empty folder and ask for the pair.
npm install -D typescript@7.0.2 typescript-eslint@8.71.0 eslint@9
npm stops with this:
npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error Found: typescript@7.0.2
npm error Could not resolve dependency:
npm error peer typescript@">=4.8.4 <6.1.0" from typescript-eslint@8.71.0
Is it TypeScript 7 that's broken? No. Run the same install with TypeScript 5.7.2 and it finishes cleanly, which is how the compatibility registry records it: status install-failed, verdict peer-range, baseline green. The limit sits in the lint package, and it hasn't moved. The registry row for 8.19.0 and the one for 8.70.0 carry the same ceiling, and 8.71.0 still does.
The blocker for TypeScript 7 isn't the compiler. It's the one package your pull request pipeline can't run without.
Why doesn't --legacy-peer-deps rescue it?
The npm error itself suggests the flag, so you'll try it. The install finishes. Then you run the linter.
typescript-eslint does not support TS 7.0.
Please see https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/#running-side-by-side-with-typescript-6.0 to run typescript-eslint using the TS 6 API.
See also https://github.com/typescript-eslint/typescript-eslint/issues/10940 for tracking typescript-eslint's support for TS >=7.1
So the peer range isn't just a cautious number. The package reads the compiler version when it loads and refuses to continue. There's a reason: TypeScript 7 is the native Go compiler, and it doesn't ship a programmatic API. The old API, the one typed lint rules read, lives in a separate package for now.
What's the fix that actually works?
Microsoft's announcement describes a compatibility package, @typescript/typescript6, that carries the TypeScript 6 API. Install it under the name typescript, and keep the native compiler under a different name.
{
"devDependencies": {
"@typescript/native": "npm:typescript@^7.0.2",
"typescript": "npm:@typescript/typescript6@^6.0.2",
"typescript-eslint": "8.71.0",
"eslint": "^9"
}
}
A plain npm install finishes with no peer conflicts. Every package that asks for typescript now receives the TypeScript 6 API, which is inside the declared range, and the native compiler arrives as a second dependency.
Here's what that tree gave in a clean reproduction on Node 22:
| Check | Result |
|---|---|
npm install | Clean, zero invalid peers |
npx tsc --version | Version 7.0.2 |
tsc6 binary | Present, runs the TS 6 compiler |
npx eslint with recommended | Flags no-explicit-any |
npx eslint with recommendedTypeChecked | Flags no-floating-promises |
That last line matters. Typed rules need a real type program, and they're the ones that silently stop firing when something goes wrong. Test it with an async function called without await and without .catch(). If the linter says nothing, your checkmark is worthless.
What changes in your scripts?
Less than you'd expect. The tsc binary now resolves to the native compiler, so tsc --noEmit in CI gets the speed gain you migrated for. The linter takes the TS 6 API through the typescript name without any config change, and projectService: true keeps working.
Two things to watch. Any script that imports typescript as a library, a codegen step or a custom AST tool, now gets version 6, not 7. That's usually what you want, since version 7 has no API to import. And upgrades are manual for a while: when typescript-eslint widens its range, you remove the alias and go back to one typescript entry.
Does this hit your repository?
Not every project meets the wall. If your lint config never loads typescript-eslint, nothing here applies. If it does, two commands tell you where you stand before you touch a version number.
npm ls typescript
npm explain typescript
npm ls shows which copy of the compiler each package resolved to, and flags anything marked invalid. npm explain goes further and prints the chain of packages that asked for it, so you can see whether the ceiling comes from typescript-eslint itself, from @typescript-eslint/parser, or from some other tool that carries its own <6.1.0 range. In the reproduction the plugin, the parser and the utility packages all carried the same cap, so one alias fixed them together.
Monorepos need one extra look. A workspace that lints with typescript-eslint and a sibling that only builds with tsc can sit on different compiler versions without anyone choosing that. Put the aliased pair in the root devDependencies and let the workspaces inherit it, otherwise one package ends up on the native compiler and its neighbour fails the install.
What if you'd rather not run two compilers?
Two choices, and neither is free.
The first is to keep lint on TypeScript 6 only. That's what the alias does in effect: the linter sees the old API and the build sees the native one. You pay for a second compiler in node_modules, and you give up nothing in rule coverage.
The second is to leave type-aware rules behind, or move to a linter that doesn't read the compiler at all. Biome and oxlint don't depend on the typescript package, so the peer range simply doesn't apply to them. The cost is rule coverage: the typed rules are the reason most teams adopted typescript-eslint, and the Biome vs ESLint and Prettier comparison covers the wider trade. Is that gap acceptable for a faster build? For some codebases yes. For anything where no-floating-promises has ever caught a real bug, probably not.
How do you keep the setup honest in CI?
The dangerous failure here isn't an error, it's silence. Typed rules can stop firing while ESLint still exits zero, so give CI a fixture that must fail. Keep a small file with one deliberate violation outside your normal lint paths, and assert that linting it returns a non-zero exit code.
! npx eslint fixtures/floating-promise.ts
The leading ! inverts the exit code, so the step passes only when ESLint reports the violation. If a future dependency bump quietly switches the type-aware rules off, this is the step that goes red. It costs a second per run and it's the only check that tests the thing you actually care about.
When does the workaround expire?
The error message names issue 10940 as the place support for TypeScript 7.1 and later is tracked, and the TypeScript 7.0 announcement says a new API is expected in 7.1. A development build, 7.1.0-dev.20261001.1, sits on the next tag today. Treat any earlier date as a guess rather than a schedule.
Until then, check the peer range before you bump anything.
npm view typescript-eslint peerDependencies
If the typescript line no longer ends in <6.1.0, the aliasing can go. If it still does, you've lost nothing by keeping it. The React migration checklist and the TypeScript 7 migration guide both point here for the lint step now.