Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt

Use this file to discover all available pages before exploring further.

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 Health OS codebase and recommends a phased adoption.

Repo baseline (as of this ADR)

ItemValue
Declared TS in package.jsontypescript@^6.0.3
Resolved TS in lockfile6.0.3 (registry tarball)
Currently installed in node_modules5.9.3 (peer-dep deduplication via npm ci --legacy-peer-deps)
Configstsconfig.json (root) → references tsconfig.app.json and tsconfig.node.json. App config: target: ES2020, module: ESNext, moduleResolution: bundler, noEmit, strict: false + strictNullChecks: true, incremental cache at node_modules/.cache/tsc/app.tsbuildinfo.
Typecheck scripttsc --noEmit -p tsconfig.app.json --incremental --tsBuildInfoFile node_modules/.cache/tsc/app.tsbuildinfo (single-config invocation; no tsc --build).
CI typechecknpm run typecheck in .github/workflows/build.yml, with node_modules/.cache/tsc cached across runs. Cold run is the slow path; warm runs hit the incremental cache.
TS Compiler API consumers3 audit scripts: scripts/audit/doc-comment-coverage/parser.ts, scripts/audit/audit-appicon-tones.ts, scripts/audit/audit-page-spacing.ts (all import ts from 'typescript').
Lintertypescript-eslint@^8.59.0 (currently installed: 8.58.2). Peer range published in installed packages: typescript >=4.8.4 <6.1.0.
Other TS-API consumers in node_modules:ts-node@10.9.2, msw@2.13.2, @vitest/*@4.1.5, vite-plugin-checker@0.13.0, eslint-plugin-tsdoc@0.5.2. (Fallow is a Rust binary and not a TS Compiler API consumer.)
ignoreDeprecationsNot set in any tsconfig.

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.
RunCompilerWall timeErrors
Cold typechecktsc (TS 6.0.3)7m 58s (478s)0
Cold typechecktsgo (TS 7.0 beta)0m 53s (53s)0
Warm typecheck (incremental cache hit)tsc (TS 6.0.3)7m 53s (no incremental cache benefit on this run; see note)0
Warm typecheck (no incremental cache; tsgo doesn’t need one)tsgo (TS 7.0 beta)3.6s0
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:
    "typecheck:fast": "cross-env NODE_OPTIONS=--max-old-space-size=8192 tsgo --noEmit -p tsconfig.app.json"
    
    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:
npm ci --legacy-peer-deps
npm install --no-save --legacy-peer-deps @typescript/native-preview@beta

# Cold tsc (TS 6.0.3)
rm -rf node_modules/.cache/tsc && mkdir -p node_modules/.cache/tsc
time ./node_modules/.bin/tsc --noEmit -p tsconfig.app.json \
     --incremental --tsBuildInfoFile node_modules/.cache/tsc/app.tsbuildinfo
# → real 7m58s, 0 errors

# Cold tsgo (TS 7.0 beta)
time ./node_modules/.bin/tsgo --noEmit -p tsconfig.app.json
# → real 0m53s, 0 errors

# Warm tsgo (no incremental cache configured)
time ./node_modules/.bin/tsgo --noEmit -p tsconfig.app.json
# → real 0m3.6s, 0 errors