The emitter is the final stage of the TypeScript compilation pipeline. It takes a type-checked AST (or a set of them, in the case of --outFile mode), applies a transformer pipeline to convert TypeScript-specific syntax into target-compatible JavaScript, and then writes the output: JavaScript files, declaration files (.d.ts), and source maps.
This page covers src/compiler/emitter.ts and its interactions with the transformer infrastructure. For the transformer pipeline itself, see Transformation Pipeline. For how the Program object initiates emission, see Program and Compilation.
The emit process has two distinct phases:
TransformerFactory functions. Each transformer rewrites TypeScript-specific or downlevel syntax into forms appropriate for the target JavaScript version and module system.Printer, which serializes nodes into text, interleaving source positions for source map generation.Emit Pipeline
Sources: src/compiler/emitter.ts446-479 src/compiler/program.ts66-68
emitFilesThe primary internal entry point is emitFiles, defined in src/compiler/emitter.ts and called by Program.emit() in src/compiler/program.ts. It receives an EmitResolver, an EmitHost, and an EmitTransformers object bundling the configured transformer factories.
forEachEmittedFileforEachEmittedFile src/compiler/emitter.ts446-479 iterates over every file that is expected to produce output. For single-file-out (--outFile) mode, it creates a Bundle node. Otherwise it iterates each SourceFile individually. For each file, it computes the output paths via getOutputPathsFor and calls the provided action.
| Output file type | Condition |
|---|---|
.js / .mjs / .cjs | emitDeclarationOnly is false |
.js.map | sourceMap or inlineSourceMap is set |
.d.ts | declaration or emitDeclarationOnly is set |
.d.ts.map | declarationMap is set |
.tsbuildinfo | incremental compilation or tscBuild is set |
Sources: src/compiler/emitter.ts446-504
The Printer interface is defined in src/compiler/types.ts. An instance is created by calling createPrinter(options, handlers) in src/compiler/emitter.ts.
Printer and its collaborators
Sources: src/compiler/types.ts src/compiler/emitter.ts1-425
Inside createPrinter, the core recursive function is pipelineEmitWithHint(hint, node). Before calling the actual node emitter, it:
onBeforeEmitNode from PrintHandlers (used by transformers for bookkeeping).substituteNode from PrintHandlers, which allows transformers to swap out a node at emit time.onEmitNode from PrintHandlers (transformers use this to inject helper prologues, etc.).emitFunctionDeclaration, emitClassDeclaration).onAfterEmitNode.This hook architecture allows the transformer pipeline to intercept every node in a non-destructive way.
Sources: src/compiler/emitter.ts
ListFormatWhen emitting node arrays (parameters, arguments, type parameters, etc.), the printer uses the ListFormat bitfield enum from src/compiler/types.ts. Key flags include:
| Flag group | Examples |
|---|---|
| Delimiter | CommaDelimited, BarDelimited, AmpersandDelimited, SemicolonDelimited |
| Whitespace | SpaceBetweenBraces, Indented, MultiLine, SingleLine |
| Brackets | Braces, Brackets, Parenthesis, AngleBrackets |
| Special | NoTrailingNewLine, OptionalIfEmpty, PreferNewLine, AllowTrailingComma |
Sources: src/compiler/types.ts
EmitFlags is a bitfield enum on src/compiler/types.ts that is stored on the EmitNode sidecar attached to each AST node (rather than directly on the node). It controls per-node printer behavior without mutating the original parsed tree.
Selected EmitFlags
| Flag | Effect |
|---|---|
NoLeadingComments | Suppress leading comment emit for the node |
NoTrailingComments | Suppress trailing comment emit |
SingleLine | Force single-line output |
MultiLine | Force multi-line output |
NoIndentation | Suppress indentation increase for children |
AsyncFunctionBody | Node is the body of an async function |
ReuseTempVariableScope | Reuse outer temp scope instead of a new one |
CustomPrologue | Statement is a custom prologue emitted before hoisted declarations |
NoHoisting | Prevent the declaration from being hoisted |
HasEndOfDeclarationMarker | End-of-declaration marker for multi-statement transforms |
Iterator | Iterate in a for-of without Symbol.iterator lookup |
NoAsciiEscaping | Emit unicode characters directly instead of as escape sequences |
The helpers setEmitFlags, addEmitFlags, and getEmitFlags in src/compiler/utilities.ts manipulate these flags through the EmitNode sidecar rather than touching the real node.
Sources: src/compiler/types.ts src/compiler/utilities.ts
EmitHint is passed as the first argument to pipelineEmitWithHint and to the substituteNode / onEmitNode hooks in PrintHandlers. It tells the printer what syntactic role the node occupies so the correct emit path is taken.
| Value | Usage |
|---|---|
Unspecified | Default; the printer determines the path from node.kind |
Expression | Emit the node as an expression (may add parentheses) |
SourceFile | Emit as a full source file |
MappedTypeParameter | Emit as a mapped type's TypeParameter |
EmbeddedStatement | Emit as a statement inside another statement |
IdentifierName | Emit as a plain identifier without keyword checks |
JsxAttributeValue | Emit in a JSX attribute value context |
Sources: src/compiler/types.ts
These two interfaces decouple the emitter from the rest of the compiler.
EmitHost (src/compiler/types.ts) is typically implemented by Program. It provides:
getSourceFiles() – the set of source filesgetCompilerOptions() – compiler optionswriteFile() – the actual file write callbackgetCommonSourceDirectory(), getCanonicalFileName(), path helpersisEmitBlocked(file) – whether a given file's emit has been suppressedEmitResolver (src/compiler/types.ts) is created by the type checker and answers semantic questions needed during emit:
isDeclarationVisible(node) – needed by the declaration transformergetReferencedExportContainer(node) – for export alias resolutionisValueAliasDeclaration(node), isReferencedAliasDeclaration(node) – import elisiongetConstantValue(node) – for const enum inlininggetTypeReferenceSerializationKind(node) – for decorator metadatacollectLinkedAliases(node) – for type-only importsWhen isolatedModules is set or type checking is skipped, a stub EmitResolver that throws on any semantic call is used instead.
Sources: src/compiler/types.ts src/compiler/checker.ts
Before printing, transformNodes (defined in src/compiler/transformer.ts, called from emitter.ts) applies the full sequence of built-in and user-provided transformers. The result is a TransformationResult holding the transformed nodes and any associated cleanup logic. The printer's PrintHandlers are wired up from the TransformationResult so that substituteNode and onEmitNode delegate to each transformer that registered hooks.
Relationship between transformers and the printer
The built-in transformers are applied in this order (for JS output):
src/compiler/transformers/ts.ts) – strips type annotations, handles decorators, emits const enum valuesuseDefineForClassFieldssrc/compiler/transformers/module/module.ts) – converts import/export to the target module formatSources: src/compiler/emitter.ts src/compiler/transformers/ts.ts src/compiler/transformers/module/module.ts
Declaration file (.d.ts) generation runs as a separate transformer pass using the declarationTransformer. This transformer:
EmitResolver.isDeclarationVisible for each declarationTypeChecker calls when explicit annotations are absent/// <reference> directives for transitive dependenciesThe declaration transformer is applied in its own call to transformNodes, producing a separate TransformationResult that is printed with a separate Printer configured for .d.ts output.
Sources: src/compiler/emitter.ts src/compiler/types.ts
Source maps track the correspondence between positions in the emitted output and positions in the original TypeScript source. The emitter uses SourceMapGenerator (created by createSourceMapGenerator) to record these mappings.
The printer calls setSourceFile and queries getSourceMapRange(node) on each node. getSourceMapRange returns a SourceMapSource (either the node's own range or an overridden one set by a transformer via setSourceMapRange). Each emitted token is recorded as a mapping segment.
For declaration maps, the same mechanism applies to the declaration transformer's printer output.
Sources: src/compiler/emitter.ts src/compiler/types.ts
getOutputPathsFor and getOwnEmitOutputFilePath in emitter.ts compute where each output file goes based on outDir, rootDir, outFile, and the input file path. The function forEachEmittedFile drives iteration and provides the computed EmitFileNames bundle { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } for each file.
.tsbuildinfo path resolution is handled by getTsBuildInfoEmitOutputFilePath src/compiler/emitter.ts481-500 which prefers tsBuildInfoFile if set, otherwise derives the path from outFile or configFilePath.
Sources: src/compiler/emitter.ts506-560
Several narrowed createPrinter wrappers in src/compiler/checker.ts and src/compiler/emitter.ts are used internally:
| Factory | Purpose |
|---|---|
createPrinter(options) | General-purpose; configures all options |
createPrinterWithDefaults() | Default options; used in checker for type display |
createPrinterWithRemoveComments() | removeComments: true; used in declaration emit |
createPrinterWithRemoveCommentsNeverAsciiEscape() | Declaration emit without ASCII escaping |
createPrinterWithRemoveCommentsOmitTrailingSemicolon() | For code-fix text generation |
Sources: src/compiler/checker.ts126-130
| Symbol | File | Role |
|---|---|---|
emitFiles | src/compiler/emitter.ts | Orchestrates the full emit for a Program |
forEachEmittedFile | src/compiler/emitter.ts | Iterates (file, outputPaths) pairs |
createPrinter | src/compiler/emitter.ts | Factory for Printer instances |
Printer | src/compiler/types.ts | Interface for AST-to-text serialization |
PrinterOptions | src/compiler/types.ts | Config for createPrinter |
PrintHandlers | src/compiler/types.ts | Hooks wired from TransformationResult |
EmitHost | src/compiler/types.ts | Abstracts file I/O and program data |
EmitResolver | src/compiler/types.ts | Semantic answers from type checker |
EmitFlags | src/compiler/types.ts | Per-node printer control flags |
EmitHint | src/compiler/types.ts | Syntactic role hint for printer |
EmitTextWriter | src/compiler/types.ts | Text buffer with indent tracking |
ListFormat | src/compiler/types.ts | Array/list formatting options |
SourceMapGenerator | src/compiler/types.ts | Records position mappings |
transformNodes | src/compiler/transformer.ts | Runs transformer chain |
TransformationResult | src/compiler/types.ts | Holds transformed nodes + hooks |
Refresh this wiki
This wiki was recently refreshed. Please wait 4 days to refresh again.