Skip to main content
Status: Proposed (recommendation: Adopt as opt-in tsgo-powered fast path now; promote to default when stable ships and the typescript-eslint peer range opens for TS 7.x) Date: 2026-04-23 Owners: Platform Architecture Related: ADR-009 Biome formatter, .github/workflows/build.yml, CI_PIPELINE.md

Context

On 2026-04-21 Microsoft published TypeScript 7.0 Beta. TS 7.0 is a Go port of the existing TypeScript codebase (“Project Corsa”), shipping under the @typescript/native-preview package and the tsgo entry point. Microsoft positions it as semantically equivalent to TS 6.0 with stableTypeOrdering on and ignoreDeprecations unset; the headline value is ~10× faster type-checking via native code and shared-memory parallelism. This ADR records a structured evaluation against the Encore OS codebase and recommends a phased adoption.

Repo baseline (as of this ADR)

What TS 7.0 actually changes for us

  • Same compiler semantics as TS 6.0 (per Microsoft’s stability statement and 10-year test suite re-run).
  • New binary name: tsgo. The typescript package will re-take the tsc entry point only when 7.0 goes stable.
  • A compatibility shim (@typescript/typescript6 exposing tsc6) lets you keep TS 6.x available for tools that import typescript as a peer.
  • stableTypeOrdering becomes the assumed default; any ignoreDeprecations usage breaks (we use neither, so this is a non-issue here).
  • No stable programmatic API in 7.0 yet — Microsoft explicitly defers that. Tools that import the TS Compiler API must keep using TS 6.x for now.

Evaluation: empirical results on this repo

We installed @typescript/native-preview@beta (tsgo v7.0.0-dev.20260421.2) alongside the existing TS 6.0.3 toolchain and compared against tsc --noEmit -p tsconfig.app.json on the same source tree, same tsconfig, same machine. Cache was cleared before each “cold” run.
Note on warm tsc: the second tsc run was effectively another cold compile because the .tsbuildinfo was generated against a tsc from the freshly installed npm tree (TS 6.0.3) and the cache was not preserved across the tsgo install in our test environment. In CI, the warm-cache path normally completes in ~8s as documented in the build workflow, which roughly matches tsgo’s warm-run number — so the truly meaningful comparison is cold ~478s → cold ~53s (~9× faster) and warm ~8s (cached) → warm ~3.6s (no cache needed) (~2× faster, with simpler cache semantics).
Diagnostics parity: zero errors emitted in either compiler for this commit. No new errors, no removed errors, no different ones. Key implication for CI: CI typecheck cold time (when the tsc-${{ runner.os }}-… cache key misses, which happens any time tsconfig*.json, package-lock.json, or src/integrations/supabase/types.ts changes — i.e., on most dependency bumps and on every Supabase types regeneration) drops from ~8 minutes to ~1 minute. Warm runs are already fast and stay fast. The Supabase types.ts regeneration is currently the single biggest CI typecheck cliff, and tsgo flattens it.

Constraints and risks

  1. typescript-eslint peer range still excludes TS ≥ 6.1.0. Installed versions in our tree advertise typescript >=4.8.4 <6.1.0. v8.58.0 added TS 6 support; TS 7 official support is expected in a future major. Until that ships, the linter must continue to resolve typescript to a 6.x build. The npm:@typescript/typescript6 alias is the supported workaround.
  2. No stable programmatic API in TS 7.0. Our three audit scripts (audit-appicon-tones.ts, audit-page-spacing.ts, doc-comment-coverage/parser.ts) import ... from 'typescript'. They must keep resolving against TS 6.x. The compatibility package handles this transparently.
  3. ts-node peer range: node_modules/ts-node@10.9.2 advertises typescript: >=2.7 so it accepts both. We use ts-node for ~15 utility scripts; keep it pointed at the same typescript resolution as the linter (TS 6.x via alias).
  4. MSW, Vitest, Vite, vite-plugin-checker, eslint-plugin-tsdoc: all consume the TypeScript package or its compiler API. None of them have published explicit TS 7 peer ranges yet. They will keep resolving against the aliased TS 6.x typescript entry — this is exactly what the Microsoft compatibility recipe is for.
  5. tsc --build (project references): Our tsconfig.json lists references but our typecheck script invokes tsc -p tsconfig.app.json directly, so build-mode caching is not in play. tsgo accepts -p. No script changes are needed beyond replacing the binary name.
  6. incremental and tsBuildInfoFile flags: tsgo does not need --incremental to be fast (warm 3.6s without one). The flags are harmless to leave in for the TS 6 fallback path; tsgo ignores or accepts them.
  7. Beta lifecycle: Microsoft plans GA “within the next two months” (i.e. on the order of June 2026), with a release candidate a few weeks before. Behavior is described as “finalized” only at RC. Until then there is non-zero risk of compatibility-affecting fixes between dev releases.
  8. No Encore-specific blocker found. We don’t use ignoreDeprecations. We don’t use removed legacy compiler options. We don’t ship a TS-emit pipeline (noEmit: true). Vite + @vitejs/plugin-react does the actual JS transform; TS is type-check only here. This makes us an unusually safe candidate for early adoption.
  9. CI cache key. The existing build workflow keys on tsconfig*.json/package-lock.json/src/integrations/supabase/types.ts. If we keep both tsc and tsgo on the same tsconfig, no key change is required. If we add a separate node_modules/.cache/tsgo path, it should be added to the cache step.

Options Considered

Option A: Stay on TS 6.0.x for the foreseeable future (status quo)

  • Pros: Zero migration work, maximal tooling compatibility, deterministic.
  • Cons: Cold typecheck remains ~8 min and is the dominant cost in CI when cache misses; developer feedback on dependency bumps and Supabase types regeneration is poor; we leave a ~9× perf win on the table on a 4,000+-file codebase that already has incremental cache as its only mitigation.
  • Pros: Zero risk to existing CI gate. Developers immediately get sub-minute cold typechecks locally. Editor experience can switch to the TypeScript Native Preview VS Code extension per developer choice. Audit scripts, linter, ts-node, and build all continue resolving the existing TS 6.x install. Empirically validated on this repo (cold 478s → 53s, no diagnostic changes).
  • Cons: CI still pays the cold-cache cost on Supabase types regeneration until we cut over the gate.

Option C: Cut CI typecheck over to tsgo immediately, install typescript as alias npm:@typescript/typescript6 so linter/audit scripts/ts-node still see TS 6.x

  • Pros: Captures the ~9× cold-CI win immediately.
  • Cons: TS 7.0 is still beta; npm install -D typescript@npm:@typescript/typescript6 plus a separate @typescript/native-preview install is a non-trivial lockfile change; warm CI is already ~8s, so the absolute CI minutes saved per run are modest unless the cold-cache path (Supabase types regen, dep bumps, branch creation) is what’s hurting. Defer to Phase 2.

Option D: Wait for TS 7.0 GA and then migrate

  • Pros: Maximum stability; benefit from RC-period fixes; ride along with typescript-eslint’s native TS 7 support when it lands.
  • Cons: ~2-month delay on developer-experience wins; no learning prior to GA; no validation against our specific audit scripts and Supabase-types regeneration cliff.

Decision (recommendation)

Adopt Option B now. Plan Option C as Phase 2 once two conditions are met: (a) TS 7.0 reaches RC or GA, (b) typescript-eslint publishes a release whose peer range includes TS 7.x. Track Phase 2 as a follow-up issue, not a blocker. Concretely:
  1. Add @typescript/native-preview as a devDependency (pinned to a known beta version, e.g. ^7.0.0-dev.20260421.2, with renovate updates allowed).
  2. Add a new npm script:
    Leave the existing typecheck script (TS 6 + tsc) unchanged as the gate.
  3. Add a brief note to AGENTS.md “Pre-Flight Checklist → Before commit” pointing developers at npm run typecheck:fast for local iteration. Keep npm run validate (which runs the slow tsc path) as the authoritative gate.
  4. Document the optional VS Code “TypeScript Native Preview” extension in docs/development/DEVELOPMENT_QUICK_REFERENCE.md as a developer-choice ergonomic upgrade.
  5. Do not modify the linter, audit scripts, ts-node, tsBuildInfoFile, or the CI cache key. Do not alias typescript. Do not change peer-dep ranges.

Phase 2 (when TS 7.0 hits RC/GA AND typescript-eslint ships TS 7-compatible peer range)

  1. Replace the root typescript dependency with the alias: "typescript": "npm:@typescript/typescript6@^6.0.0" (keeps the API for linter, audit scripts, MSW, Vitest, ts-node, and vite-plugin-checker).
  2. Add the stable typescript@^7.x dependency separately, exposing tsc (the TS 7 entry point).
  3. Switch npm run typecheck and CI’s “Typecheck” step to use the new tsc (TS 7).
  4. Drop the --incremental/--tsBuildInfoFile flags from the typecheck script (no longer needed for performance), and remove node_modules/.cache/tsc from the cache step. Optionally add a smaller node_modules/.cache/tsgo cache entry if measurement shows it helps.
  5. Update AGENTS.md §“Pre-Flight Checklist → Before commit”, CI_PIPELINE.md, and the typecheck section of .github/workflows/build.yml comments.
  6. Run npm run validate and the full unit/integration/RLS smoke baseline before merging.

Phase 3 (post-Phase-2 cleanup)

  1. Once typescript-eslint, ts-node, msw, vitest, vite-plugin-checker, and eslint-plugin-tsdoc all advertise TS 7-compatible peer ranges, drop the @typescript/typescript6 alias and the audit-script keep-alive comments; let typescript resolve to 7.x natively.

Consequences

Positive

  • Sub-minute cold typecheck for developers immediately (Phase 1) — biggest single ergonomic win on this repo since incremental cache.
  • Zero behavior delta in our codebase (validated: identical zero-error result on this commit).
  • ~7-minute cold-CI win once Phase 2 lands (every Supabase types regeneration, dep bump, or PR with a tsconfig change saves ~7 minutes of CI wall time).
  • tsgo removes the need to babysit --incremental/tsBuildInfoFile cache invalidation.

Negative / Risks

  • Two compilers coexist locally during Phase 1 (small node_modules footprint cost; one extra binary).
  • Phase 2 timing depends on third-party (typescript-eslint) release cadence.
  • Beta dev-stream releases of tsgo change frequently; we should pin and let renovate roll.
  • Until Phase 2, npm run typecheck continues to be the ~8-minute cold path in CI.

Mitigations

  • Phase 1 is intentionally additive: nothing existing changes.
  • The cutover (Phase 2) is gated on RC/GA + linter compatibility, so we don’t ship a beta into the CI gate.
  • All TS Compiler API consumers (3 audit scripts plus 6 third-party tools) are explicitly enumerated above; the @typescript/typescript6 alias keeps every one of them working.
  • ignoreDeprecations is not in use anywhere, removing the most common 6→7 break.

Empirical evidence (commands and results)

Reproducible on a fresh clone of this commit on a Linux x64 dev box: