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:
- Validates options. Catches mutually-exclusive flags, invalid combinations, and unknown options.
- Selects the host. A
CompilerHostis provided by the caller or defaulted fromts.sysviacreateCompilerHost. - Resolves root files. Each
rootNameis loaded via the host'sgetSourceFile. Triple-slash references andimportspecifiers are resolved transitively; the closure of files is the program's source-file set. - Adds
lib.*.d.ts. Based on--lib,--target, and--noLib, the right declaration files are auto-included. - Reuses old program structure. If an
oldProgramwas passed, files whose import resolutions are unchanged are reused verbatim. - 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:
- Options diagnostics (errors in
tsconfig.json). - Global diagnostics (e.g., missing lib files).
- Per-file syntactic diagnostics.
- Per-file semantic diagnostics.
- 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 viacreateWatchProgramin watch mode.tsserver— everyProjectowns aLanguageServiceHostthat lazily createsPrograms.- Build mode —
createSolutionBuilderconstructs oneProgramper project reference. - Embedded users — bundlers like Vite and tools like
ts-loadercreate their ownPrograminstances via the public API.
Entry points for modification
- Resolution changes —
moduleNameResolver.tsandresolutionCache.ts. - New file inclusion logic (e.g., another implicit
lib.*.d.ts) —processImportedModules/processReferencedFilesinprogram.ts. - Emit-time options —
emitand theEmitResolverit 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.