Skip to main content

Debugging Compiler Code

Using VS Code

1

Build the Compiler

First, build TypeScript with source maps:
hereby local
Source maps are enabled by default and allow debugging TypeScript source.
2

Create a Test File

Create a TypeScript file to compile:
test.ts
const message: string = "Hello, TypeScript!";
console.log(message);
3

Add Launch Configuration

Create .vscode/launch.json:
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug tsc",
      "program": "${workspaceFolder}/built/local/tsc.js",
      "args": ["test.ts"],
      "sourceMaps": true,
      "outFiles": [
        "${workspaceFolder}/built/**/*.js"
      ]
    }
  ]
}
4

Set Breakpoints

Open compiler source files (e.g., src/compiler/checker.ts) and set breakpoints.
5

Start Debugging

Press F5 or select Run > Start Debugging.
Use the customDescriptionGenerator setting for better object inspection:
"customDescriptionGenerator": "'__tsDebuggerDisplay' in this ? this.__tsDebuggerDisplay(defaultValue) : defaultValue"

Using Chrome DevTools

Debug with Chrome’s inspector:
node --inspect-brk built/local/tsc.js test.ts
Then:
  1. Open Chrome and navigate to chrome://inspect
  2. Click Inspect next to your Node process
  3. DevTools opens with source maps
  4. Set breakpoints and step through code
Use --inspect-brk to break at the first line, or --inspect to break at breakpoints only.

Command-Line Debugging

For quick debugging, add console logs:
src/compiler/checker.ts
function getTypeOfSymbol(symbol: Symbol): Type {
    console.log("Checking symbol:", symbol.escapedName);
    // ... rest of function
}
Then rebuild and run:
hereby local
node built/local/tsc.js test.ts

Debugging Tests

Interactive Test Debugging

Run tests with the inspector:
hereby runtests --tests=<testName> -i
Example:
hereby runtests --tests=asyncFunctions -i
This starts the test with --inspect-brk, pausing before execution.

VS Code Test Debugging

The provided launch configuration enables debugging test files:
1

Set Up Configuration

Copy the template:
cp .vscode/launch.template.json .vscode/launch.json
2

Open Test File

Open a test file in tests/cases/compiler/ or tests/cases/conformance/.
3

Set Breakpoints

Add breakpoints in:
  • The test file itself
  • Compiler source files (src/**)
4

Launch

Press F5 or use Run > Start Debugging.The configuration automatically:
  • Builds test infrastructure (npm: build:tests)
  • Runs Mocha with the current file name
  • Enables source maps
  • Uses smart stepping

Launch Configuration Details

.vscode/launch.json
{
  "type": "node",
  "request": "launch",
  "name": "Mocha Tests (currently opened test)",
  "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
  "args": [
    "-u", "bdd",
    "--no-timeouts",
    "--colors",
    "built/local/run.js",
    "-f", "${fileBasenameNoExtension}"
  ],
  "sourceMaps": true,
  "smartStep": true,
  "preLaunchTask": "npm: build:tests"
}
Key settings:
  • --no-timeouts - Prevents tests from timing out during debugging
  • -f ${fileBasenameNoExtension} - Filters to current file name
  • smartStep: true - Steps over uninteresting code
  • preLaunchTask - Rebuilds tests before each debug session

Debugging Language Service

Debugging tsserver

The TypeScript language server runs as a separate process in editors. Here’s how to debug it:
1

Set TSServer Log Level

In VS Code settings:
{
  "typescript.tsserver.log": "verbose"
}
2

View Logs

Command Palette → TypeScript: Open TS Server log
3

Attach Debugger

Find the tsserver process ID:
ps aux | grep tsserver
In .vscode/launch.json:
{
  "type": "node",
  "request": "attach",
  "name": "Attach to tsserver",
  "processId": "${command:PickProcess}"
}
Launch and select the tsserver process.

Language Service Features

Debug specific language service operations:
// Set breakpoint in src/services/completions.ts
export function getCompletionsAtPosition(
    host: LanguageServiceHost,
    program: Program,
    // ...
) {
    console.log("Getting completions at", position);
    // ... implementation
}

Debugging Techniques

Conditional Breakpoints

In VS Code, right-click a breakpoint and set a condition:
symbol.escapedName === "myVariable"
Or log a message without stopping:
console.log("Symbol:", symbol.escapedName), false

Call Stack Navigation

The TypeScript compiler has deep call stacks. Use these techniques:
  1. Smart Step - Skip getter/setter properties
  2. Step Into Target - Choose which function to step into
  3. Restart Frame - Rerun a function from the start

Watching Expressions

Add watches for common TypeScript objects:
// Watch the type of a node
checker.getTypeAtLocation(node)

// Watch the symbol of an identifier
checker.getSymbolAtLocation(identifier)

// Watch flags
node.flags & NodeFlags.Const

Debug Output

Enable detailed compiler output:
# Trace resolution
node built/local/tsc.js --traceResolution test.ts

# Show types
node built/local/tsc.js --noEmit --extendedDiagnostics test.ts

Performance Debugging

Tracing Compiler Performance

Generate performance traces:
node built/local/tsc.js --generateTrace trace-output test.ts
View traces in Chrome:
  1. Open chrome://tracing
  2. Load the generated JSON file
  3. Analyze compilation time

Extended Diagnostics

Show detailed timing information:
node built/local/tsc.js --extendedDiagnostics test.ts
Output includes:
  • Files read
  • Parse time
  • Bind time
  • Check time
  • Emit time
  • I/O operations

Profiling with Node

Generate CPU profiles:
node --prof built/local/tsc.js test.ts
node --prof-process isolate-*.log > profile.txt
Or use Chrome DevTools profiler:
node --inspect built/local/tsc.js test.ts
Open DevTools and use the Profiler tab.

Memory Debugging

Heap Snapshots

Capture memory usage:
node --inspect built/local/tsc.js test.ts
In Chrome DevTools:
  1. Go to Memory tab
  2. Take heap snapshot
  3. Analyze object retention

Memory Leaks

Increase memory limit for debugging:
node --max-old-space-size=8192 built/local/tsc.js test.ts

Tracking References

Use WeakMaps and WeakSets to track object relationships without preventing garbage collection.

Common Debugging Scenarios

Set breakpoints in:
  • src/compiler/checker.ts - Main type checker
  • getTypeOfSymbol() - Symbol type resolution
  • checkExpression() - Expression type checking
  • checkSourceFile() - File-level checking
Watch expressions:
checker.typeToString(type)
checker.symbolToString(symbol)
Set breakpoints in:
  • src/compiler/parser.ts - Main parser
  • parseSourceFile() - Entry point
  • parseStatement() - Statement parsing
  • parseExpression() - Expression parsing
Watch:
scanner.getTokenText()
scanner.getToken()
Set breakpoints in:
  • src/compiler/emitter.ts - JavaScript emitter
  • src/compiler/declarationEmitter.ts - .d.ts emitter
  • emitSourceFile() - File emit entry
  • emit() - Node emit dispatcher
Enable trace output:
node built/local/tsc.js --traceResolution test.ts 2>&1 | tee resolution.log
Set breakpoints in:
  • src/compiler/moduleNameResolver.ts
  • resolveModuleName()
  • loadModuleFromFile()
Set breakpoints in src/compiler/transformers/:
  • ts.ts - TypeScript-specific transforms
  • es2015.ts - ES2015 transforms
  • esnext.ts - ESNext transforms
  • jsx.ts - JSX transforms

Debugging Tools

Built-in Utilities

TypeScript provides debug helpers:
// Pretty-print a node
Debug.printNode(node);

// Assert conditions
Debug.assert(condition, "Error message");

// Fail fast
Debug.fail("Should not reach here");

// Check values
Debug.checkDefined(value);
Debug.assertNever(value);

Stack Trace Limits

Increase stack trace depth:
hereby runtests --tests=<test> --stackTraceLimit=100
Or unlimited:
hereby runtests --tests=<test> --stackTraceLimit=full

Source Maps

Verify source maps are working:
# Check for .js.map files
ls built/local/*.js.map

# Validate source map
node -e "console.log(require('./built/local/tsc.js.map'))"

Remote Debugging

Debug TypeScript running in a remote environment:
# On remote machine
node --inspect=0.0.0.0:9229 built/local/tsc.js test.ts

# Create SSH tunnel from local machine
ssh -L 9229:localhost:9229 user@remote

# Connect with Chrome DevTools to localhost:9229

Troubleshooting Debugging

Verify:
  • Source maps are enabled
  • Files are built (hereby local)
  • Breakpoint is in reachable code
  • Using correct Node version (>= 14.17)
Rebuild with:
hereby clean
hereby local
Source maps may be missing or incorrect:
# Check for source maps
ls built/local/*.js.map

# Rebuild
hereby clean
hereby local --bundle=false
Use unbundled mode for better debugging.
  • Disable all breakpoints except critical ones
  • Use conditional breakpoints
  • Use logpoints instead of breakpoints
  • Disable source map stepping in non-TypeScript code
Use console or watch expressions:
// Watch expression
JSON.stringify(obj, null, 2)

// Or use __tsDebuggerDisplay
obj.__tsDebuggerDisplay?.()

Resources

VS Code Debugging

Official VS Code debugging guide

Node.js Debugging

Node.js debugging documentation

Chrome DevTools

Chrome DevTools reference

TypeScript Compiler Notes

Architectural documentation

Next Steps

Testing

Learn how to write and run tests

Building

Build system reference

Build docs developers (and LLMs) love