Last Updated: 2026-04-28Documentation Index
Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
Use this file to discover all available pages before exploring further.
Applies to: Encore Health OS (v0.9.0-alpha, TypeScript 6.0.3, ~8,400
.ts/.tsx files)
TL;DR — Numbers
| Scenario | Time | Notes |
|---|---|---|
Cold npm run typecheck | ~8m 48s | No .tsbuildinfo cache |
Warm npm run typecheck | ~12s | Incremental cache hits |
| CI warm (Turbo cache hit) | ~230ms | Full skip via content-addressed cache |
tsgo (TypeScript Go preview) cold | ~58s | 8.5× faster than tsc, no incremental needed |
tsgo warm | ~3s |
Root Cause: What Makes This Codebase Slow
Runningtsc --extendedDiagnostics on a cold build reveals:
#1: The 107k-line generated Supabase types.ts (primary bottleneck)
src/integrations/supabase/types.ts is 3.4 MB and defines a single Database type encoding 1,126 tables × 3 operation shapes (Row/Insert/Update) = 3,378 table-type variants, all nested inside deeply-parameterized generic helpers (TablesInsert<T>, TablesUpdate<T>, Enums<T>, etc.).
- 672 source files directly import from it
- 1,866 uses of
Database['public']['Tables']['...']['Row']indexing - 7,426
.from(tableName)calls — every call instantiates the fullDatabase<>generic tree against the table-name union
.from('ce_activities') it must:
- Resolve
tableNameas"ce_activities"against the 1,039-member string union of all table names - Distribute
Database['public']['Tables'][typeof tableName]across all 1,126 branches - Check assignability for every instantiation
#2: 317 files use type intersections (&) instead of interface extends
TypeScript’s wiki explicitly calls this out: intersections are checked structurally every time (O(N²) in members), while interface extends results are cached. Files like src/cores/gr/types/index.ts (1,140 lines, 198 exports) frequently define types as A & B & { ... }.
#3: 392 barrel index.ts files
Barrel files don’t cause check-time overhead directly, but they dramatically increase the module graph that TypeScript must resolve and bind before checking begins. The biggest ones (src/cores/gr/types/index.ts, src/cores/rh/types/index.ts) re-export hundreds of types that may already be inlined elsewhere, forcing the checker to track them across module boundaries repeatedly.
#4: Tests mixed into the Turbo input glob
turbo.json lists tests/**/*.ts and tests/**/*.tsx as inputs to the typecheck task, but tsconfig.app.json only includes src/. This means Turbo correctly invalidates its cache when test files change — even though tsc never actually type-checks them in the app project. A stray test file (tests/utils/supabase-test-client.ts) is included because it is transitively reachable from a src/ import via path resolution.
#5: No strict mode / inferred return types
Withstrict: false and noImplicitAny: false, TypeScript must infer return types for almost every function. Complex functions that return supabase.from(...) chains have inferred return types that cascade into many re-instantiations of the Database generics.
Recommendations — Prioritized
Priority 1 — Immediate (hours of effort, high impact)
1a. Migrate to tsgo (TypeScript 7 Go native preview) for local dev and pre-commit
The TypeScript team has ported the compiler to Go (@typescript/native-preview). Tested on this exact codebase:
| Time | |
|---|---|
tsc cold | 8m 48s |
tsgo cold | 58s |
tsgo warm | ~3s |
7.0.0-dev.*) and should not replace tsc in the authoritative CI gate yet — tsgo currently has minor compatibility gaps. However, it is safe to offer it as an opt-in developer speedup:
typecheck:fast for the interactive developer workflow, keeping typecheck (plain tsc) in CI. This alone cuts pre-commit wait time from ~12s (warm tsc) to ~3s.
Risk: tsgo may miss a small number of errors that tsc catches. CI must keep tsc as the authoritative gate. As tsgo reaches stability (expected TypeScript 7.0 stable), flip CI to tsgo as well.
1b. Add verbatimModuleSyntax: true to tsconfig.app.json
This flag makes all type-only imports require import type, which:
- Eliminates the compiler’s need to determine at emit time whether an import is value or type
- Reduces the number of files that must be “checked together” vs checked in isolation
- Is already enforced by the Biome linter rule
useImportType— this makes it a compiler-enforced invariant too
allowJs: true (set in root tsconfig.json) is incompatible with verbatimModuleSyntax in some edge cases. Since tsconfig.app.json does not enable allowJs, this is safe.
1c. Narrow the Turbo typecheck input glob to exclude test files
Currently turbo.json lists tests/**/*.ts and tests/**/*.tsx as inputs, which means any test file change invalidates Turbo’s typecheck cache even though tsconfig.app.json only checks src/. Change it:
tests/** globs that were previously listed have been removed. turbo.json does not support JSON comments, so this note lives here instead. This allows Turbo to skip the typecheck entirely on PRs that only touch test files, which is a common pattern.
Priority 2 — Medium (days of effort, very high long-term impact)
2a. Split src/integrations/supabase/types.ts into per-core type modules
The 107k-line monolithic types.ts is the single biggest driver of the 31M instantiations. Each Database['public']['Tables']['x'] access forces TypeScript to evaluate every table branch.
Recommended approach:
- Generate per-core type slice files (e.g.,
src/integrations/supabase/types.ce.ts,types.hr.ts, etc.) containing only the tables owned by each core. - Each core imports from its own slice file instead of the monolithic one.
- Reduce the
Databasetype to a minimal “plumbing” type used only by the Supabase client itself.
2b. Replace type X = A & B patterns with interface X extends A, B
In files like src/cores/gr/types/index.ts, src/cores/rh/types/index.ts, and src/platform/permissions/constants.ts, convert intersection types to interface extension wherever possible:
2c. Add explicit return type annotations to complex functions
Functions that callsupabase.from(...) chains without return type annotations force TypeScript to re-infer the full generic return type on every call site. Adding explicit Promise<SomeRow[]> or PostgrestQueryBuilder<...> annotations breaks the inference chain:
docs:comments:audit:changed script already enforces TSDoc on exported APIs. This is the same principle applied to return types of functions that interact with the Supabase client.
Priority 3 — Architectural (weeks of effort, maximum long-term impact)
3a. TypeScript Project References with composite: true
Break the monolithic tsconfig.app.json into per-core projects that reference each other. This is the TypeScript team’s recommended approach for large codebases.
tsc -b (build mode):
- Each sub-project produces a
.d.tsdeclaration file (not the full source types) - Downstream projects check against the declaration, not the full source
- Changed files only re-check their sub-project and any downstream projects that directly depend on them
composite: truerequiresdeclaration: true, which is incompatible withnoEmit: true— you would need to emit.d.tsfiles into a temp location (e.g.,node_modules/.cache/tsc-declarations/)- The existing architecture enforcement (no core-to-core imports) maps cleanly to project reference boundaries
- Estimated setup effort: 2–3 days to author the per-core configs and validate the dependency graph
- Expected outcome:
tsc -b --incrementalcold time drops from ~9min to ~1–2min; warm stays ~10s
3b. Migrate to tsgo (TypeScript 7 Go) as the primary CI type-checker
Once TypeScript 7.0 stable is released (expected late 2026), migrate the typecheck CI step from tsc to tsgo. As demonstrated above, this is a near drop-in replacement that delivers ~8-10× speedup with no code changes.
Track readiness at: https://github.com/microsoft/typescript-go
Quick-Win Configuration Changes (Apply Now)
These changes can be made immediately with zero risk:1. Add typecheck:fast script using tsgo
2. Add verbatimModuleSyntax to tsconfig.app.json
3. Fix the Turbo input glob for typecheck
4. Pre-commit hook: use typecheck:fast for developer iteration
In .husky/pre-commit or the lint-staged config, replace npm run typecheck with npm run typecheck:fast for the interactive pre-commit check. Keep npm run typecheck in CI.
Measurement Commands
Use these to track progress:Summary Table
| Recommendation | Effort | Impact | Risk |
|---|---|---|---|
Add typecheck:fast with tsgo | 1 hour | ★★★★★ — 8.5× cold speedup | Low — CI keeps tsc |
| Fix Turbo input glob | 30 min | ★★★ — skip on test-only PRs | None |
Add verbatimModuleSyntax | 1 hour | ★★ — reduces redundant checking | Low |
Split types.ts by core | 2–3 days | ★★★★★ — eliminates 31M instantiations | Medium — requires gen tooling |
Convert & to extends | 1–2 days | ★★★ — caches intersection results | Low |
| Explicit return types on Supabase fns | 2–3 days | ★★★★ — breaks inference cascade | Low |
| Full project references | 1 week | ★★★★★ — structural + incremental | High — large config change |
Upgrade to tsgo/TS7 in CI | Future | ★★★★★ — 10× with no code changes | Wait for stable |