Open-Source Wikis

/

TypeScript

/

Systems

/

Transformers

microsoft/TypeScript

Transformers

The pipeline that turns a type-checked TypeScript AST into a target-flavoured JavaScript AST. Transformers handle TypeScript-specific syntax (interfaces, type annotations, parameter properties), JSX, decorators (legacy and Stage-3), and a target-version-specific stack of ECMAScript downlevelers. A separate transformer pipeline produces declaration-only .d.ts output.

Source

The transformer pipeline is split between two files and a directory:

Path Role
src/compiler/transformer.ts Pipeline composer — chooses which transformers run for the current options
src/compiler/visitorPublic.ts The visitor primitives (visitNode, visitEachChild, visitNodes) every transformer uses
src/compiler/transformers/ One file per transformer

The transformer files (sorted by size):

Transformer File Role
transformES2015 es2015.ts (5,065 lines) Lower classes, arrow functions, template strings, destructuring, spread, etc. to ES5
transformClassFields classFields.ts (3,360 lines) Public/private fields, decorators-aware initialisation
transformGenerators generators.ts (3,284 lines) Lower generator functions to a state-machine for ES5
transformLegacyDecorators legacyDecorators.ts TypeScript's original experimentalDecorators
transformESDecorators esDecorators.ts TC39 Stage-3 decorators
transformJsx jsx.ts <Foo />createElement(Foo) / jsx(Foo)
transformTypeScript ts.ts Strip type annotations, lower enums, namespaces, parameter properties
transformDeclarations declarations.ts (3,000+ lines) and declarations/ Build .d.ts output
transformES2016transformESNext es2016.tsesnext.ts One per ECMAScript edition
Module transformers module/ commonjs, system, esnext lowering
transformDestructuring destructuring.ts Used by ES2015 and class fields
transformNamedEvaluation namedEvaluation.ts Class/function name binding (TC39 named evaluation)
transformTaggedTemplate taggedTemplate.ts Tagged-template-literal lowering
transformClassThis classThis.ts Capture and rewrite this references in nested arrows

Purpose

Each transformer is a TransformerFactory<T>: a function (context: TransformationContext) => Transformer<T> where Transformer<T> is (node: T) => T. Transformers are composed; a SourceFile flows through them in a fixed order.

The order matters because some transformers introduce constructs that later transformers must lower further. For example, transformESDecorators expands @dec class C {} into ES2022-style classes-with-static-blocks; transformClassFields then lowers static blocks to IIFEs if --target is below ES2022.

How it works

graph TD
    SF["SourceFile (typed AST)"] --> Pipeline["transformer.ts: choose pipeline"]
    Pipeline --> TS["transformTypeScript: strip types, lower enums/namespaces"]
    TS --> Decorators{"decorator mode?"}
    Decorators -->|legacy| Legacy["transformLegacyDecorators"]
    Decorators -->|stage-3| Stage3["transformESDecorators"]
    Legacy --> CF["transformClassFields"]
    Stage3 --> CF
    CF --> JSX{"JSX enabled?"}
    JSX -->|yes| TJsx["transformJsx"]
    JSX -->|no| Skip
    TJsx --> Down["downlevel transformers (ESNext → ES2022 → ... → ES2015)"]
    Skip --> Down
    Down --> Mods["module transformer (CommonJS / SystemJS / ESM)"]
    Mods --> Final["target-shaped tree → emitter"]
    SF --> Decl["transformDeclarations"]
    Decl --> Emitter
    Final --> Emitter

Three high-level paths exist:

  1. JS emit — the chain that produces .js. The chain length depends on --target and --module.
  2. Declaration emittransformDeclarations runs in parallel and produces .d.ts.
  3. Custom transformers — embedders (e.g., bundlers) can inject their own before / after / afterDeclarations factories via program.emit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers).

transformer.ts orchestration

src/compiler/transformer.ts is small (~700 lines) and mostly contains the getModuleTransformer switch that picks transformECMAScriptModule, transformImpliedNodeFormatDependentModule, transformSystemModule, or transformModule (CommonJS) based on CompilerOptions.module and the file's impliedNodeFormat.

The file also wires up the TransformationContext — the state object every transformer is given. The context exposes:

  • factory for building nodes
  • addEmitHelper to request emit helpers
  • enableSubstitution / enableEmitNotification for cross-cutting hooks (used heavily by transformES2015)
  • hoistVariableDeclaration, startLexicalEnvironment, endLexicalEnvironment for moving declarations out of nested scopes
  • getEmitResolver to ask the checker about resolved bindings

Visitor primitives

visitorPublic.ts provides the primitives every transformer is built on:

  • visitNode(node, visitor) — apply visitor to one node
  • visitEachChild(node, visitor, context) — copy a node, applying visitor to its children
  • visitNodes(nodes, visitor) — visit a node array
  • visitFunctionBody, visitParameterList, etc. — sugar for the most common patterns

The primitives use the factory's update* methods, which short-circuit when no children change. This means a transformer that doesn't touch a subtree returns the original instance, preserving identity for downstream caches.

Decorators

The split between legacyDecorators.ts and esDecorators.ts lets users opt into either the original TypeScript-flavoured experimentalDecorators (still supported, still common) or the standards-track Stage-3 implementation. The two transformers don't run together; --experimentalDecorators selects one or the other. See features/decorators.

JSX

transformJsx rewrites JSX elements/fragments into either React.createElement calls (the classic transform) or jsx/jsxs/jsxDEV calls (the new transform), depending on --jsx. See features/jsx.

Declaration emit

transformDeclarations is special. It walks the type-checked tree and produces a declaration-only tree containing only types, signatures, and ambient declarations — no implementation. The output is a .d.ts file that reflects the public surface of the input file. See features/declaration-emit.

Integration points

  • Called by program.emit (in program.ts) for every file selected for emit.
  • Reads CompilerOptions to choose the pipeline.
  • Reads the checker's EmitResolver for late-bound questions.
  • Outputs feed directly into the emitter.

Entry points for modification

  • New downlevel target: add a transformer file under transformers/ and wire it into the pipeline in transformer.ts. Update getEmitScriptTarget and LanguageFeatureMinimumTarget in src/compiler/types.ts.
  • Modifying decorators: edit legacyDecorators.ts or esDecorators.ts only. Don't fold the changes into transformTypeScript.
  • Adding emit helpers: extend src/compiler/factory/emitHelpers.ts and request them from the appropriate transformer via context.requestEmitHelper.

See systems/emitter for what consumes the transformer output.

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

Transformers – TypeScript wiki | Factory