Open-Source Wikis

/

TypeScript

/

Systems

/

Node factory

microsoft/TypeScript

Node factory

The single canonical builder for AST nodes. Every parsed, transformed, or emitted node ultimately passes through the factory; this is what guarantees consistent node shape, parent pointers, position tracking, and emit-helper attachment across the compiler.

Source

File Lines Role
src/compiler/factory/nodeFactory.ts 7,542 The full factory — one createXxx per node kind
src/compiler/factory/baseNodeFactory.ts 60 Allocator hook for node objects
src/compiler/factory/nodeTests.ts ~37k isXxx predicates for every node kind
src/compiler/factory/parenthesizerRules.ts ~34k Where to insert parentheses on emit to preserve precedence
src/compiler/factory/emitHelpers.ts ~69k Standard helpers (__awaiter, __decorate, __spread, …) injected by transformers
src/compiler/factory/emitNode.ts ~13k Per-node emit metadata (comments, source maps, helpers)
src/compiler/factory/utilities.ts ~76k Higher-level builders used by transformers
src/compiler/factory/nodeChildren.ts tiny Lookup of named children for diagnostics

Purpose

ts.factory is the single object exposed by the public API for building nodes. The parser uses an internal factory variant; the transformers use the public one; the emitter consumes whatever factory produced. Going through a single factory ensures:

  • pos/end defaulting — synthesised nodes have pos === end === -1 so the emitter knows to skip source-map entries.
  • parent linkingsetParent and setParentRecursive keep parent pointers consistent.
  • flagsNodeFlags and emit-only flags (EmitFlags) are merged correctly when one node is built from another.
  • Helper requests — when a transformer creates a __awaiter call, the factory records that the helper must be emitted at the file level. The emitter later prints it.
  • Comment and source-map preservationsetOriginalNode, setTextRange, setSyntheticLeadingComments are factory-level concerns.

How it works

graph LR
    Caller["parser / transformer / fix"] --> Factory["factory.createXxx"]
    Factory --> Base["baseNodeFactory.allocate"]
    Base --> Node["new Node"]
    Factory --> Init["set kind, flags, modifiers, children"]
    Factory --> Helpers["request emit helpers"]
    Factory --> Tests["nodeTests.isXxx (consumer side)"]
    Factory --> Pretty["parenthesizerRules (emitter side)"]

Sub-factories

Different callers receive different factory variants:

  • The parser factory tags created nodes with their position and disables expensive invariants like helper tracking.
  • The default factory (used by the transformers) enables synthesis defaults (pos === end === -1) and helper tracking.
  • The lazyTextNodeFactory is used by some incremental paths.

createNodeFactory(flags, baseFactory) builds the variant. The NodeFactoryFlags enum controls invariants:

  • NoIndentationOnFreshPropertyAccess — emit hint
  • NoOriginalNode — don't auto-link originalNode

nodeTests.ts

The companion to nodeFactory.ts is a giant table of predicates: isVariableDeclaration(n), isClassExpression(n), etc. Together they let any consumer build new trees and check incoming trees without manual n.kind === SyntaxKind.X switching. The two files are the most heavily generated-feeling part of the codebase, and changes to either typically come together.

parenthesizerRules.ts

When an emitter prints a + b * c, it shouldn't add parens around b * c (precedence handles it), but when it prints (a + b) * c it must. The parenthesizer encodes operator precedence and associativity rules so the emitter can ask parenthesizer.parenthesizeBinaryOperand(...) and let the rules decide. Transformers also call the parenthesizer when they hoist an expression to a different precedence context.

emitHelpers.ts

When transformES2015 lowers async function f() {} to ES5, it emits a call to __awaiter. The helper itself isn't generated inline every time; instead the transformer requests it from the factory, and the emitter prints the canonical helper definition once at the top of the file (or imports it from tslib when --importHelpers is set). This file contains the canonical text of every helper.

Integration points

Entry points for modification

Adding a new node kind requires touching all of factory/:

  1. Add a SyntaxKind and interface in src/compiler/types.ts.
  2. Add createXxx, updateXxx, and any cloneXxx to nodeFactory.ts.
  3. Add an isXxx to nodeTests.ts.
  4. Add precedence/associativity rules to parenthesizerRules.ts if it's an expression.
  5. Update forEachChild in parser.ts and visitEachChild in visitorPublic.ts to walk children.

See systems/transformers and systems/emitter for the consumers.

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

Node factory – TypeScript wiki