Today Platform Web — Dev Docs
Toolchain

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:

  1. A consumer (e.g. apps/web) reads the source of @todayai-labs/ui directly, bypassing the export surface that downstream npm consumers see
  2. Errors in declaration emission — broken export aliases, circular type refs — are silently ignored
  3. The CI build doesn't match what tsc --build (and tsgo --build) does under Vercel's next 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 = OpalShaderShell

tsc --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:

  1. Read the first error in the log — that's the root cause
  2. The rest of the TS6305 chain disappears once the root is fixed
  3. If the log shows TS6305 with no other errors, run moon run :typecheck --updateCache to 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.

On this page