loke.dev
Two calibrated compiler mechanisms run on parallel rails and feed one shared verification gauge

Run TypeScript 7 Without Breaking typescript-eslint

Fix TypeScript 7 ESLint peer conflicts and Cjs crashes by running TypeScript 7 beside the TypeScript 6 compatibility API, with a verified npm setup.

Published 5 min read

TypeScript 7 can type-check a project while the same dependency update makes npm ci reject typescript-eslint or makes ESLint crash with Cannot read properties of undefined (reading 'Cjs'). The compiler is working. The problem is that TypeScript 7.0 does not ship the programmatic API that current lint and framework tools expect.

The current fix is to install TypeScript 7 for the tsc command and keep TypeScript 6 under the package name that tools import. This is a transition setup, not a permanent fork of the project.

The working package layout

Microsoft's TypeScript 7 release guidance recommends the @typescript/typescript6 compatibility package when utilities still need the old API. The package exports TypeScript 6's API and a tsc6 executable. TypeScript 7 can then be installed under an npm alias that supplies the normal tsc command.

This package.json shape was verified on August 2, 2026 with TypeScript 7.0.2, @typescript/typescript6 6.0.2, ESLint 10.6.0, and typescript-eslint 8.63.0.

{
  "private": true,
  "type": "module",
  "scripts": {
    "lint": "eslint .",
    "typecheck": "tsc --noEmit",
    "typecheck:legacy": "tsc6 --noEmit"
  },
  "devDependencies": {
    "@typescript/native": "npm:typescript@7.0.2",
    "eslint": "10.6.0",
    "typescript": "npm:@typescript/typescript6@6.0.2",
    "typescript-eslint": "8.63.0"
  }
}

The names do different jobs:

typescript is the TypeScript 6 compatibility package. typescript-eslint resolves this package name when it imports the compiler API.

@typescript/native is an npm alias for TypeScript 7. It installs the TypeScript 7 tsc executable without taking over the package name used by API consumers.

tsc6 remains available for a direct comparison or a temporary legacy check.

Why a normal TypeScript 7 upgrade breaks lint

The typescript-eslint support issue reproduces two failures with TypeScript 7.0.2 and typescript-eslint 8.63.0. npm ci rejects the dependency tree because the published peer range requires TypeScript below 6.1.0. If installation proceeds, ESLint crashes while reading a compiler API value named Cjs.

The issue also reports that the TypeScript 7 build succeeds. That split is the useful diagnostic: tsc can work while a tool that imports TypeScript as a library fails.

Do not use --force or --legacy-peer-deps as the final fix. Those options can move npm past the peer check, but they do not create the missing API. The later Cjs crash is evidence that the mismatch is real, not only stale package metadata.

Add the split setup without losing a clean lockfile

Make the dependency change on a small branch. Use the package manager already declared by the repository, regenerate its lockfile once, and review which package owns each executable.

For npm, install the aliases directly or edit package.json to match the reviewed versions. The commands below are illustrative; keep exact versions if reproducible CI is the goal.

npm install -D \
  typescript@npm:@typescript/typescript6@6.0.2 \
  @typescript/native@npm:typescript@7.0.2

Then install the repository's existing ESLint and typescript-eslint versions normally. Do not add a second package manager or a separate lockfile for this migration.

Prove which compiler each task runs

Run the binaries before trusting script names.

npx tsc --version
npx tsc6 --version
npm run lint
npm run typecheck
npm ci

In the verified minimal project, tsc reported 7.0.2, tsc6 reported 6.0.3 from the compatibility package, and lint plus both type checks passed. npm ls showed TypeScript 7 under @typescript/native and @typescript/typescript6 under the package name typescript.

The compatibility package version and the version printed by its binary are separate details, so record both instead of assuming they are identical. The important contract is that API consumers resolve the compatibility package while the main tsc command resolves TypeScript 7.

Keep tsconfig compatible with TypeScript 7

The side-by-side install fixes the missing API boundary. It does not restore compiler options removed in TypeScript 7. Microsoft positioned TypeScript 6 as the transition release and says options deprecated there are removed in TypeScript 7.

Run the TypeScript 6 check without ignoreDeprecations before changing the CI default. Clear the warnings and errors it identifies, then compare the TypeScript 6 and 7 diagnostics. A package alias should not be used to hide a real compiler configuration migration.

Framework and monorepo boundaries

Microsoft says Vue, MDX, Astro, Svelte, and similar embedded-language workflows may still need TypeScript 6 because their tooling embeds the old API. Angular template tooling has the same constraint. TypeScript 7 can still be useful for a separate project-wide CLI check, but do not replace the framework's supported type-check command with tsc and assume template diagnostics are covered.

Nx publishes a TypeScript 6 and 7 side-by-side guide because @nx/js/typescript, Vite, and typescript-eslint still use the programmatic API. Nx projects should follow that executor-specific setup rather than replacing an Nx target with a generic shell command.

For a plain npm project, keep the boundary visible in scripts: lint uses the TypeScript 6 API through typescript-eslint, typecheck uses TypeScript 7, and typecheck:legacy is a temporary comparison. For a framework project, retain the framework's own checker as another explicit task.

Know when to remove the compatibility package

Microsoft expects TypeScript 7.1 to provide a new programmatic API. That does not mean every tool will support it on release day. Remove the split only after the tools in this repository declare support and the normal clean-install, lint, framework check, and type-check tasks pass without the TypeScript 6 alias.

Track the dependency instead of leaving the alias forever. A small maintenance note should name why @typescript/typescript6 exists, which packages still require it, and the issue or release that will trigger a retest.

The useful outcome is simple: TypeScript 7 runs the fast project check, existing tools keep the API they support, and CI proves both paths from a clean install.