TypeScript with tsgo
Why this repo runs `tsgo --build`, not `tsc --noEmit` — and the two error codes (TS4023, TS6305) you only see under the native compiler.
The repo uses tsgo — the
TypeScript Native Preview compiler shipped as @typescript/native-preview in
the catalog. Every project's typecheck task is:
{
"scripts": {
"typecheck": "tsgo --build --pretty"
}
}pnpm typecheck at the repo root runs moon run :typecheck which fans this
out across every workspace project. Do not use tsc --noEmit — it is
literally a different binary and catches a different set of errors.
What --build does that --noEmit doesn't
tsgo --build (and tsc --build before it) walks the TypeScript project
references graph declared in each tsconfig.json's references: array. It
emits each project's .d.ts declaration files in dependency order, then
typechecks downstream consumers against those emitted declarations.
tsc --noEmit skips declaration emit entirely. That means:
- A consumer (e.g.
apps/web) reads the source of@todayai-labs/uidirectly, bypassing the export surface that downstream npm consumers see - Errors in declaration emission — broken export aliases, circular type refs — are silently ignored
- The CI build doesn't match what
tsc --build(andtsgo --build) does under Vercel'snext build
CI runs moon run :typecheck which runs tsgo --build. Locally using
tsc --noEmit would give you a false green that CI then rejects.
Two errors you only see under --build
TS4023 — Exported variable's type references an unexported interface
// LoginShell.tsx
import { OpalShaderShell, type OpalShaderShellProps } from '@todayai-labs/opal'
// This is the error case — alias exports the value, but its inferred
// type references OpalShaderShellProps which is not re-exported.
export const LoginShell = OpalShaderShelltsc --noEmit sees this and shrugs (the value resolves; the type compiles
in-memory). tsgo --build has to emit a .d.ts for LoginShell, and that
declaration would mention OpalShaderShellProps — which is not exported
from the file. So:
error TS4023: Exported variable 'LoginShell' has or is using name 'OpalShaderShellProps' from external module but cannot be named.
Fix: export the interface explicitly:
export interface OpalShaderShellProps {
/* ... */
}TS6305 — Output file X has not been built from source file Y
error TS6305: Output file '.../dist/foo.d.ts' has not been built from
source file '.../src/foo.ts'.This is always a cascade. An upstream project failed to emit its
declarations (often because of TS4023, a syntax error in a .d.ts template,
or a missing composite: true), and every downstream project that depends
on it sees stale or missing .d.ts files.
Diagnostic procedure:
- Read the first error in the log — that's the root cause
- The rest of the TS6305 chain disappears once the root is fixed
- If the log shows TS6305 with no other errors, run
moon run :typecheck --updateCacheto bust Moon's cache and re-emit
Project references
Every project's tsconfig.app.json extends @todayai-labs/tsconfig/base.json:
{
"extends": "@todayai-labs/tsconfig/base.json",
"compilerOptions": {
"rootDir": ".",
"outDir": "dist/ts/app",
"tsBuildInfoFile": "dist/ts/app/.tsbuildinfo"
}
}Base config (packages/tsconfig/base.json) sets composite: true,
incremental: true, noEmitOnError: true, strict mode, and ES Next target.
The root tsconfig.json lists every project's tsconfig.json in its
references: so tsgo --build knows the full dependency graph.
When you add a new project, register it in the root tsconfig.json — see
tsconfig-references for the layout rule.
Why tsgo and not tsc
@typescript/native-preview is the upcoming Rust-backed TypeScript compiler.
It is significantly faster (typecheck times in the hundreds of ms for projects
that took seconds under tsc), and produces the same diagnostics. The catalog
pins a specific dev build; upgrading is one line in pnpm-workspace.yaml.
If tsgo ever segfaults or produces a wrong error, the workaround is to
pnpm exec tsc --build --pretty and report the divergence upstream. So far
this hasn't been necessary in this repo.
Moon task orchestration
Moon defines per-project build / typecheck / test tasks and the dependency graph between them. The root pnpm scripts are thin wrappers over `moon run :*`.
tsconfig project references
Three tsconfig files per project — root / app / node — extending a single shared base. Why each one exists and what goes in it.