How to Access SourceFile and Program Objects in TSSLint RuleContext for AST Traversal

TSSLint exposes TypeScript's SourceFile and Program objects directly through the RuleContext parameter, allowing rules to perform deep AST traversal and type-aware analysis without external imports.

TSSLint is a TypeScript-first linter built on top of the official compiler API in the johnsoncodehk/tsslint repository. When you define custom lint rules, the framework automatically injects a RuleContext object that contains everything needed for sophisticated code analysis, including the current source file and entire program instance for accessing the type checker.

RuleContext Properties for TypeScript API Access

The RuleContext interface bundles four essential properties for rule authors. These are defined in [packages/types/index.ts](https://github.com/johnsoncodehk/tsslint/blob/master/packages/types/index.ts#L44-L48):

  • typescript: The full TypeScript namespace (typeof import('typescript')), giving you access to all utility functions like ts.forEachChild and ts.isIdentifier.
  • file: The ts.SourceFile instance representing the current file being linted.
  • program: The ts.Program instance containing the entire project context, type checker, and compiler options.
  • report: A helper function to emit diagnostics with optional quick fixes.

This design eliminates the need to manually import TypeScript or create program instances in your rules.

Traversing the AST with SourceFile

The file property serves as the entry point for AST traversal. Since it is a standard ts.SourceFile, you can use TypeScript's built-in visitor functions to walk the node tree.

Here is a complete example demonstrating how to traverse the AST and flag specific identifiers:

import { defineRule } from '@tsslint/config';

export default defineRule(({ typescript: ts, file, program, report }) => {
  // Walk the AST of the current file
  ts.forEachChild(file, function walk(node) {
    // Example: flag any use of `eval`
    if (ts.isIdentifier(node) && node.text === 'eval') {
      report('Avoid using eval()', node.getStart(file), node.getEnd())
        .withFix('Remove eval', () => [{
          fileName: file.fileName,
          textChanges: [{ 
            newText: '', 
            span: { 
              start: node.getStart(file), 
              length: node.getWidth(file) 
            } 
          }],
        }]);
    }

    // Continue walking deeper
    ts.forEachChild(node, walk);
  });
});

This pattern is demonstrated in the repository's built-in example at [fixtures/noConsoleRule.ts](https://github.com/johnsoncodehk/tsslint/blob/master/fixtures/noConsoleRule.ts#L3-L14), which shows how to destructure the context and access these objects for AST inspection.

Accessing Type Information via Program

While file provides the syntax tree, the program property enables semantic analysis. By calling program.getTypeChecker(), you gain access to type information, symbol resolution, and compiler diagnostics.

export default defineRule(({ typescript: ts, file, program, report }) => {
  const checker = program.getTypeChecker();
  
  ts.forEachChild(file, function walk(node) {
    if (ts.isCallExpression(node)) {
      const signature = checker.getResolvedSignature(node);
      const returnType = checker.getReturnTypeOfSignature(signature);
      
      // Perform type-level checks here
      if (checker.typeToString(returnType) === 'any') {
        report('Avoid implicit any return type', node.getStart(file), node.getEnd());
      }
    }
    ts.forEachChild(node, walk);
  });
});

The program object also exposes getCompilerOptions(), allowing rules to adapt behavior based on project configuration such as strict mode settings or path mappings.

How the Context is Constructed

Understanding the internal construction of RuleContext helps when debugging complex rules. The context object is assembled in two key locations:

  1. Context Builder: [packages/config/lib/tsl.ts](https://github.com/johnsoncodehk/tsslint/blob/master/packages/config/lib/tsl.ts) constructs the base context object, injecting program, file, and the typescript namespace.
  2. Rule Execution: [packages/core/index.ts](https://github.com/johnsoncodehk/tsslint/blob/master/packages/core/index.ts) obtains the Program from the TypeScript language service and creates the final RuleContext for each file being analyzed.

The defineRule function exported from [packages/config/index.ts](https://github.com/johnsoncodehk/tsslint/blob/master/packages/config/index.ts) wraps your rule function and ensures these parameters are passed correctly at runtime.

Summary

  • RuleContext exposes file (SourceFile), program (Program), typescript (TS namespace), and report (diagnostic emitter) to every rule.
  • Use file with ts.forEachChild() to traverse the AST and inspect syntax nodes.
  • Access program.getTypeChecker() for semantic analysis, type resolution, and symbol information.
  • The context is type-defined in packages/types/index.ts and instantiated in packages/config/lib/tsl.ts and packages/core/index.ts.

Frequently Asked Questions

How do I access the TypeScript type checker in a TSSLint rule?

Access the type checker by calling program.getTypeChecker() on the program object destructured from the RuleContext. This returns a TypeChecker instance that can resolve types, symbols, and signatures for any node in the file being analyzed.

What is the difference between file and program in RuleContext?

The file property is the specific ts.SourceFile currently being linted, used for AST traversal and obtaining node positions. The program property represents the entire TypeScript project context, containing all source files, the type checker, and compiler options.

Where is the RuleContext interface defined in TSSLint?

The RuleContext interface is defined in [packages/types/index.ts](https://github.com/johnsoncodehk/tsslint/blob/master/packages/types/index.ts#L44-L48). This file specifies the exact types for typescript, program, file, and report properties available to every rule.

Can I use Program to access compiler options within a rule?

Yes. Call program.getCompilerOptions() to access the resolved compiler options for the project. This allows rules to adapt their behavior based on configuration flags like strict, noImplicitAny, or custom path mappings defined in tsconfig.json.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →