Open-Source Wikis

/

Deno

/

Systems

/

Type checking

denoland/deno

Type checking

Active contributors: David Sherret, Nayeem Rahman, Bartek Iwańczuk

Purpose

Deno can type-check TypeScript before running it (deno check, deno run --check, the LSP, deno test's default check, …). Two implementations live side by side:

  1. The bundled tsc — a snapshotted copy of the official TypeScript compiler running inside a dedicated JS isolate.
  2. tsgo — the newer TypeScript-Go rewrite, run as an external subprocess and spoken to over IPC.

The orchestration layer in cli/type_checker.rs (1,138 lines) and cli/tsc.rs decides which to use, marshals the module graph in, and surfaces diagnostics.

Directory layout

cli/
├── type_checker.rs           # 1,138 lines: top-level orchestrator
├── tsc.rs                    # bundled tsc driver (snapshot + IPC)
├── tsc/                      # the bundled tsc JS sources (snapshotted)
│   ├── 00_typescript.js      # the literal TypeScript compiler bundle
│   └── …
├── lsp/tsc.rs                # 7,137 lines: LSP-specific tsc bridge
└── lsp/ts_server.rs          # tsc subprocess plumbing for the LSP

libs/typescript_go_client/    # IPC client for tsgo
└── lib.rs                    # protocol + runner

Two paths

Path A — bundled tsc

cli/tsc/00_typescript.js is a snapshotted bundle of the official TypeScript compiler. At build time, cli/snapshot/ runs it through V8's snapshot machinery so that deno check doesn't have to re-parse the compiler at startup.

When type-checking, the CLI:

  1. Builds the module graph (see Module loading).
  2. Hands the graph to a child JsRuntime running the snapshotted tsc.
  3. Configures tsc to read source via custom callbacks (so it sees the graph the CLI built rather than going to disk).
  4. Collects diagnostics, formats them, and exits with non-zero on errors.

This is the long-standing default. The driver is cli/tsc.rs; the LSP-specific equivalent is cli/lsp/tsc.rs (which uses the same compiler bundle but with editor-style request/response, including incremental updates).

Path B — tsgo

tsgo is the TypeScript team's Go rewrite of tsc. It runs as an out-of-process subprocess. The crate libs/typescript_go_client (libs/typescript_go_client/lib.rs) implements the IPC protocol:

  • Spawn the tsgo binary
  • Send the module graph + compiler options over stdio (length-prefixed JSON or similar)
  • Stream diagnostics back

Adopting tsgo is incremental — at this snapshot of the codebase both paths are present and selectable. The motivation is that Go-tsc is dramatically faster for cold-start type checks.

Key abstractions

Type File Role
TypeChecker cli/type_checker.rs Top-level orchestrator; chooses tsc vs tsgo, builds the graph, formats diagnostics
TypeCheckCache cli/type_checker.rs + cli/cache/ Keys on graph hash so repeat checks don't re-run tsc
Tsc (in cli/tsc.rs) cli/tsc.rs Bundled-tsc driver: spawns the embedded JS isolate, calls into the snapshot
TsServer (in cli/lsp/tsc.rs) cli/lsp/tsc.rs LSP-specific bridge with incremental updates
TsgoClient (concept) libs/typescript_go_client/lib.rs IPC client for the tsgo subprocess
Diagnostic TypeScript's own diagnostic type Marshalled across the boundary; surfaced as deno errors

How it works

graph TD
    Cmd["deno check / deno run --check / LSP"] --> TC["TypeChecker<br/>cli/type_checker.rs"]
    TC --> Graph["ModuleGraph<br/>(cli/graph_util.rs)"]
    TC --> Decide{"tsc or tsgo?"}
    Decide -->|tsc| TscDriver["cli/tsc.rs"]
    TscDriver --> Snapshot["bundled tsc snapshot<br/>cli/tsc/00_typescript.js"]
    Snapshot --> TscDiag["TS Diagnostic[]"]
    Decide -->|tsgo| TsgoIpc["libs/typescript_go_client"]
    TsgoIpc --> TsgoProc["tsgo subprocess"]
    TsgoProc --> TsgoDiag["TS Diagnostic[]"]
    TscDiag --> TC
    TsgoDiag --> TC
    TC --> Cache["TypeCheckCache<br/>cli/cache/"]
    TC --> Format["formatted diagnostics"]
    Format --> User["stderr / LSP publishDiagnostics"]

Caching

Type checking is expensive. The CLI caches results keyed on a hash of:

  • The module graph (every module's content hash)
  • Compiler options (from deno.json compilerOptions and CLI flags)
  • TypeScript / tsgo version

If the cache key matches, deno check exits in milliseconds without invoking the compiler. The cache lives under DENO_DIR/check/.

Updating the bundled TypeScript

Updating cli/tsc/00_typescript.js is a deliberate process documented in tools/update_typescript.md. Steps include:

  1. Replace the bundled 00_typescript.js from a fresh build of TypeScript.
  2. Update cli/tsc/dts/ if the lib *.d.ts files moved.
  3. Re-run the snapshot generator (cli/snapshot/).
  4. Update spec tests for any output format changes (TypeScript's diagnostic messages do drift between minor versions).

Integration points

  • CLI: deno checkcli/tools/check.rsTypeChecker (cli/type_checker.rs).
  • Run with check: cli/tools/run/ calls TypeChecker before evaluation when --check is on.
  • LSP: uses cli/lsp/tsc.rs for editor-friendly incremental checking; the runner-level type checker isn't used because the LSP needs its own caching strategy.
  • Compile / Bundle: deno compile and deno bundle invoke the type checker before serializing the graph.

Entry points for modification

  • Diagnostic message changes — typically come from TypeScript itself; changes to formatting live in cli/type_checker.rs.
  • A new compiler option — add it to the CLI's CheckFlags (cli/args/flags.rs), thread through cli/type_checker.rs, and pass to whichever driver is selected.
  • Tsgo IPC protocollibs/typescript_go_client/lib.rs (and the matching server side, which lives outside this repo).
  • Cache invalidationcli/cache/ for the disk format; TypeCheckCache in cli/type_checker.rs for the keying logic.

Key source files

File Purpose
cli/type_checker.rs Top-level orchestrator (1,138 lines)
cli/tsc.rs Bundled-tsc driver (snapshot + IPC)
cli/tsc/ The bundled tsc JS bundle and dts files
cli/lsp/tsc.rs LSP-specific tsc bridge (7,137 lines)
cli/lsp/ts_server.rs tsc subprocess plumbing
cli/cache/ On-disk cache for type-check results
libs/typescript_go_client/ tsgo IPC client crate
tools/update_typescript.md Manual update procedure

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Type checking – Deno wiki | Factory