Open-Source Wikis

/

TypeScript

/

Systems

/

Emitter

microsoft/TypeScript

Emitter

The pretty-printer that turns the post-transformer AST into .js, .d.ts, and source-map bytes on disk. Also the home of --declaration emit and .tsbuildinfo writing.

Source

File Lines Role
src/compiler/emitter.ts 6,378 Tree → text printer
src/compiler/sourcemap.ts 1,000+ Source-map (V3) generation
src/compiler/transformers/declarations.ts 3,000+ Builds the .d.ts AST that the emitter prints
src/compiler/expressionToTypeNode.ts ~70k Type-to-syntax conversion used by declaration emit
src/compiler/builder.ts 116k Incremental emit + .tsbuildinfo

Purpose

The emitter is a printer plus an orchestrator. Its responsibilities:

  • Print every Node kind into target-flavoured JavaScript text.
  • Apply EmitFlags and per-node emit metadata (synthetic comments, source-map ranges, noSubstitution markers).
  • Emit source maps using V3 mappings.
  • Emit declarations (.d.ts) by running transformDeclarations and then printing.
  • Emit .tsbuildinfo files for incremental compilation.
  • Decide where each output file goes (outDir, outFile, rootDir, declarationDir).

The emitter does not type-check. It assumes the tree it received is already valid and post-transformation. The most subtle thing it does is apply parenthesisation, comment placement, and substitution hooks.

Key abstractions

Symbol File Role
createPrinter(options?, handlers?) emitter.ts Public API to print arbitrary nodes; used by code fixes
Printer emitter.ts Internal printer state (writer, source-map generator, current node, helpers)
emitFiles emitter.ts Internal entry called by program.emit
Writer emitter.ts Buffered text writer (line/column tracking, indentation)
SourceMapGenerator sourcemap.ts Builds V3 mappings
EmitNode, EmitFlags factory/emitNode.ts Per-node emit metadata
noSubstitution, enableSubstitution, enableEmitNotification transformer.ts Cross-cutting print-time hooks

How it works

graph TD
    Tree["transformed tree (per file)"] --> Print["Printer.printNode"]
    Print --> Emit["emitTextOfNode"]
    Emit --> Comments["pipelineEmitWithComments"]
    Comments --> SM["pipelineEmitWithSourceMaps"]
    SM --> Hint["pipelineEmitWithHint"]
    Hint --> Sub["substituteNode (transformer hooks)"]
    Sub --> Notify["onEmitNode (transformer hooks)"]
    Notify --> Switch["switch on SyntaxKind"]
    Switch --> Children["recurse for children"]
    Children --> Out["writer text"]

The printer organises printing as a pipeline per node:

  1. Decide what comments to emit before/after.
  2. Emit the source-map opener.
  3. Apply emit hint (Expression vs. Statement vs. JsxAttributeValue etc.) to choose the right grammar context.
  4. Apply transformer-installed substitution and notification hooks.
  5. Switch on SyntaxKind to print the construct itself.
  6. Recurse for children, with parenthesisation rules from factory/parenthesizerRules.ts.
  7. Close the source-map span and trailing comments.

Source maps

Source-map generation lives in sourcemap.ts. It produces V3 source maps with optional --sourceRoot rewriting, --inlineSourceMap for embedded maps, and --inlineSources for embedded original text. Mappings are recorded by the emitter at every node boundary; the generator deduplicates and base64-VLQ encodes them at flush time.

Declaration emit

--declaration triggers transformDeclarations (src/compiler/transformers/declarations.ts). Declaration emit is type-aware: it asks the checker for the type of unannotated exports and synthesises .d.ts representations. Type-to-node conversion uses expressionToTypeNode.ts to produce minimal, portable types. See features/declaration-emit.

Incremental emit

When --incremental (or --composite) is enabled, builder.ts records per-file signatures and writes a .tsbuildinfo file. On subsequent runs, only files whose dependencies changed are re-emitted. builder.ts is large (~116k lines including utilities) because it must conservatively decide which downstream files to invalidate for each change in a way that remains correct under structural typing.

Pretty-printing rules

  • Line endings\r\n on Windows by default; --newLine LF forces Unix-style.
  • Indentation — tabs vs spaces and width come from the printer options (and ultimately tsconfig.json's deprecated --indent).
  • Strict adherence to original positions — when emitting unmodified subtrees, the printer copies the source text verbatim (preserving comments and whitespace).
  • Module emit — module-system specifics come from the transformer (not the emitter); the emitter just prints whatever module shape the transformer produced.

Integration points

Entry points for modification

  • Adding a new node-print case: extend the switch in emitter.ts. New SyntaxKinds require entries here or output is "[no print]" placeholder text.
  • Tweaking helper-printing: see emitFile and emitHelpers paths.
  • Source-map changes: sourcemap.ts.
  • Declaration emit shape changes: transformers/declarations.ts and expressionToTypeNode.ts.

See systems/transformers for what feeds the emitter and systems/program for the orchestration.

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

Emitter – TypeScript wiki | Factory