Open-Source Wikis

/

TypeScript

/

Systems

/

Parser

microsoft/TypeScript

Parser

A recursive-descent parser that turns the scanner's token stream into a SourceFile AST. The parser is incremental: when given a previous SourceFile and a TextChangeRange, it reuses unchanged subtrees and re-parses only what's affected.

Purpose

The parser produces every TypeScript syntax tree node and is the canonical owner of node pos, end, parent, and flags. It also records:

  • Comment directives (// @ts-ignore, // @ts-expect-error, // @ts-nocheck) for downstream filtering.
  • Pragmas like /// <reference />, /// <amd-module />, JSDoc reference tags.
  • JSDoc comments, parsed into the same tree under each node's .jsDoc property.

Source

File Lines Role
src/compiler/parser.ts 10,823 The whole parser (incl. JSDoc subset)
src/compiler/factory/nodeFactory.ts 7,542 Node creation primitives the parser uses
src/compiler/types.ts 10,670 Node and SyntaxKind definitions

Key abstractions

Symbol Role
createSourceFile(fileName, sourceText, options) Public entry — parses a .ts / .js / .d.ts file into a SourceFile
updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks?) Incremental update
Parser (internal namespace) Holds parser state and grammar productions
JSDocParser (internal namespace) JSDoc subset, invoked inline
IncrementalParser (internal namespace) Reuse logic for incremental parses
parseJsonText(fileName, text) JSON / JSONC → SourceFile (used for tsconfig.json)
parseIsolatedEntityName(text, languageVersion) Used by name-resolution code paths that get a string and need an entity-name node

The parser is implemented as a single closure-rich namespace; no class is exported. Its state (scanner, current token, context flags, factory) is held in let-bound module-level variables and reset between files.

Grammar contexts

Parsing is driven by context flags (NodeFlags.YieldContext, AwaitContext, DecoratorContext, DisallowInContext, …). Production functions like parseExpression consult these flags to decide what's grammatically permitted at the current position. tryParse and lookAhead save and restore scanner state so the parser can speculatively try one production and fall back if it misses.

graph TD
    Text["source text"] --> Scanner["Scanner.scan"]
    Scanner --> Loop["Parser main loop"]
    Loop --> Stmts["parseStatements"]
    Stmts --> Decls["parseDeclaration"]
    Stmts --> Exprs["parseExpression"]
    Decls --> Factory["factory.createXxx"]
    Exprs --> Factory
    Factory --> SF["SourceFile (root)"]
    Loop --> JSDoc["JSDoc sub-parser"]
    JSDoc --> Factory
    SF --> Out["SourceFile + parseDiagnostics"]

Incremental parsing

The parser caches every node's pos, end, and parent. When the user types a character in an editor, tsserver calls updateSourceFile with a TextChangeRange. The incremental parser:

  1. Walks the old tree to find subtrees fully outside the change range — these can be reused verbatim, with pos/end shifted by the text-length delta.
  2. Re-parses subtrees that intersect the change range from scratch, splicing them into the otherwise-reused tree.
  3. Reuses the original parseDiagnostics for unchanged regions.

This makes typing in even very large files cheap — the typical edit re-parses only the function or class containing the cursor.

Error recovery

Parsing must succeed on every input, including malformed code. The parser uses several strategies:

  • Synthetic tokens — when an expected token is missing, the parser reports a diagnostic and inserts a Missing placeholder so children continue to type-check.
  • Recovery tokens — production-specific lists of tokens that signal "abandon this production and pop up a level" (e.g., } ends a block).
  • Reparse fallbacks — JSX vs. less-than expression, arrow-function vs. parenthesised expression, and similar ambiguous constructs use lookAhead to commit only after disambiguation.

Comment directives like // @ts-ignore are recorded by the parser into commentDirectives on the SourceFile. The checker reads these to suppress diagnostics on the next line.

JSDoc

The parser's JSDocParser namespace is invoked when a /** … */ comment is encountered. JSDoc has its own grammar (@param {Foo} bar - description, @returns, …), supports a TypeScript-flavoured type subset, and produces specialized JSDoc* node kinds. The result is attached to the next "real" node via .jsDoc. The JSDoc tree is what powers --allowJs/--checkJs JS-with-types support.

Diagnostics

Per-file parse errors live on SourceFile.parseDiagnostics. The checker later merges them with bind diagnostics, semantic diagnostics, and suggestion diagnostics to produce the final user-visible list. Diagnostic locations come from token positions and are precise to the character.

Integration points

Entry points for modification

  • New syntax: add productions to the appropriate parseXxx functions in parser.ts, update forEachChild and the visitor in src/compiler/visitorPublic.ts, add factory methods to nodeFactory.ts, and add type predicates to nodeTests.ts.
  • Improving error recovery: search for parseErrorAtCurrentToken and createMissingNode to find the existing recovery patterns.
  • Touching JSDoc: the JSDocParser namespace inside parser.ts.

See systems/binder for the next pipeline stage and primitives/source-file for what the parser produces.

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

Parser – TypeScript wiki | Factory