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 |
transformES2016 … transformESNext |
es2016.ts … esnext.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 --> EmitterThree high-level paths exist:
- JS emit — the chain that produces
.js. The chain length depends on--targetand--module. - Declaration emit —
transformDeclarationsruns in parallel and produces.d.ts. - Custom transformers — embedders (e.g., bundlers) can inject their own
before/after/afterDeclarationsfactories viaprogram.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:
factoryfor building nodesaddEmitHelperto request emit helpersenableSubstitution/enableEmitNotificationfor cross-cutting hooks (used heavily bytransformES2015)hoistVariableDeclaration,startLexicalEnvironment,endLexicalEnvironmentfor moving declarations out of nested scopesgetEmitResolverto 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 nodevisitEachChild(node, visitor, context)— copy a node, applying visitor to its childrenvisitNodes(nodes, visitor)— visit a node arrayvisitFunctionBody,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(inprogram.ts) for every file selected for emit. - Reads
CompilerOptionsto choose the pipeline. - Reads the checker's
EmitResolverfor 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 intransformer.ts. UpdategetEmitScriptTargetandLanguageFeatureMinimumTargetinsrc/compiler/types.ts. - Modifying decorators: edit
legacyDecorators.tsoresDecorators.tsonly. Don't fold the changes intotransformTypeScript. - Adding emit helpers: extend
src/compiler/factory/emitHelpers.tsand request them from the appropriate transformer viacontext.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.