Open-Source Wikis

/

TypeScript

/

Systems

/

Program

microsoft/TypeScript

Program

The Program is the central object that knows about a complete TypeScript compilation: source files, references, options, the type checker, and the emit. Every other public-API entry point ultimately produces or consumes a Program.

Source

src/compiler/program.ts — 5,201 lines, plus closely related siblings:

File Role
program.ts createProgram, Program, file/diagnostic orchestration
programDiagnostics.ts Stable per-file diagnostic queues and tsconfig-level errors
resolutionCache.ts Memoises module/type-reference resolution across rebuilds
moduleNameResolver.ts The resolution algorithms themselves
watchPublic.ts, watch.ts, watchUtilities.ts Watch-mode wrappers around Program
tsbuildPublic.ts, tsbuild.ts Solution Builder for tsc -b

Purpose

Construct a Program and you have:

  • Resolved file list — including triple-slash references and module imports.
  • A type checker (lazily created on first call).
  • A diagnostic stream (parse → bind → semantic → suggestion).
  • An emit() function that drives the transformer + emitter pipeline.

Programs are immutable. Watch mode and the language service produce sequences of programs; each new program shares unchanged source files with its predecessor (structureIsReused === StructureIsReused.Completely is the fast path).

Key abstractions

Symbol Role
createProgram(rootNames, options, host?, oldProgram?, configFileParsingDiagnostics?) Public factory
createProgram(programOptions: CreateProgramOptions) Object-style overload
Program The compilation interface
CompilerHost Filesystem + name-resolution abstraction (readFile, getSourceFile, resolveModuleNames, …)
EmitResult What program.emit() returns: emitted file list, diagnostics, emitSkipped
Program.emit(targetSourceFile?, writeFile?, cancellationToken?, emitOnlyDtsFiles?, customTransformers?) Run the transformer + emitter pipeline
Program.getSemanticDiagnostics(sourceFile?, cancellationToken?) Type-check the program
Program.getSyntacticDiagnostics, getDeclarationDiagnostics, getOptionsDiagnostics Other diagnostic streams
Program.getTypeChecker() Lazily create and return the checker
BuilderProgram (in builder.ts) A Program wrapper that tracks file-affecting changes for .tsbuildinfo

How it works

graph TD
    Opts["CompilerOptions + rootNames"] --> Resolve["Resolve root files via host"]
    Resolve --> Walk["Walk imports + references"]
    Walk --> MR["moduleNameResolver / resolutionCache"]
    MR --> ParseAll["For each file: parse via parser.ts"]
    ParseAll --> Lib["Add lib.*.d.ts files"]
    Lib --> Bound["Bind on demand"]
    Bound --> Program["Program object"]
    Program --> CheckLazy["Lazy checker"]
    Program --> EmitFn["emit()"]
    EmitFn --> Trans["transformer.ts"]
    Trans --> Emit["emitter.ts"]

The constructor:

  1. Validates options. Catches mutually-exclusive flags, invalid combinations, and unknown options.
  2. Selects the host. A CompilerHost is provided by the caller or defaulted from ts.sys via createCompilerHost.
  3. Resolves root files. Each rootName is loaded via the host's getSourceFile. Triple-slash references and import specifiers are resolved transitively; the closure of files is the program's source-file set.
  4. Adds lib.*.d.ts. Based on --lib, --target, and --noLib, the right declaration files are auto-included.
  5. Reuses old program structure. If an oldProgram was passed, files whose import resolutions are unchanged are reused verbatim.
  6. Sets up diagnostic streams. Parse-time diagnostics come from SourceFile.parseDiagnostics; bind diagnostics come from the binder; semantic diagnostics come from the checker.

Old-program reuse

tryReuseStructureFromOldProgram is the main optimisation. It walks the old program's resolutions and, for every file whose tsconfig hasn't changed and whose imports resolve identically, reuses the parsed SourceFile and bind state. If anything has changed structurally (new imports, changed module resolution, changed lib), it falls back to fully reloading.

This is what makes tsserver and --watch mode fast: 99% of files survive each program rebuild.

Diagnostic ordering

Diagnostics are produced in a fixed order so user-facing output is stable:

  1. Options diagnostics (errors in tsconfig.json).
  2. Global diagnostics (e.g., missing lib files).
  3. Per-file syntactic diagnostics.
  4. Per-file semantic diagnostics.
  5. Suggestion diagnostics (language service only).

Emit

program.emit() runs transformer.ts then the emitter for each non-declaration file (or for a single targetSourceFile). Optional customTransformers allow embedders to inject before, after, and afterDeclarations factories.

const result = program.emit(
  undefined,
  /*writeFile*/ undefined,
  /*cancellationToken*/ undefined,
  /*emitOnlyDtsFiles*/ false,
  customTransformers
);
console.log(result.emitSkipped, result.diagnostics);

Skip reasons include: unresolved compile errors with --noEmitOnError, only .d.ts requested but no --declaration, etc.

Integration points

  • tsc — invoked once for one-shot compilation, repeatedly via createWatchProgram in watch mode.
  • tsserver — every Project owns a LanguageServiceHost that lazily creates Programs.
  • Build modecreateSolutionBuilder constructs one Program per project reference.
  • Embedded users — bundlers like Vite and tools like ts-loader create their own Program instances via the public API.

Entry points for modification

  • Resolution changes — moduleNameResolver.ts and resolutionCache.ts.
  • New file inclusion logic (e.g., another implicit lib.*.d.ts) — processImportedModules/processReferencedFiles in program.ts.
  • Emit-time options — emit and the EmitResolver it builds for the emitter.
  • Watch behaviour — see systems/watch-and-builder.

See systems/checker, systems/transformers, systems/emitter, and systems/module-resolution for the parts the program orchestrates.

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

Program – TypeScript wiki | Factory