TSSLint `.at()` Method: Advanced Rule Debugging and Stack Trace Customization

The .at() method captures error objects and stack frame indices to attach detailed debug information to diagnostics, enabling precise tracing of rule failures back to their source code location.

The tsslint repository provides a flexible linting framework that prioritizes developer experience when debugging complex rules. The .at() method on the Reporter object allows rule authors to preserve execution context and customize how stack traces appear in diagnostic output, making it an essential tool for advanced rule debugging.

How the .at() Method Works in TSSLint Core

The .at() method is a chainable modifier available on the Reporter object that every rule receives via the report() helper in packages/core/index.ts. When invoked, it stores an error reference and stack depth index that the framework uses to generate rich diagnostic information.

The Reporter Chain Architecture

When a rule reports a diagnostic, the report(message, start, end) function returns a Reporter instance that supports method chaining. The .at(err, stackIndex) modifier accepts two parameters:

  • err: An Error object (either the original thrown error or a newly created one) whose stack trace will be preserved
  • stackIndex: A number indicating which frame in the stack trace should be treated as the entry point for the diagnostic

This design enables fluent API usage:

report('Avoid using eval()', evalPos, evalPos + 4)
  .at(err, 0)
  .asError()
  .withFix('Remove eval', () => [{ /* edits */ }]);

Internal Storage and Lazy Evaluation

Internally, the Reporter stores the error and stack index as a tuple [Error, number] in a private variable called location. When the diagnostic's relatedInformation property is accessed, it lazily invokes the repository-provided getRelatedInformations(location[0], location[1]) function.

This lazy evaluation, implemented in packages/core/index.ts, ensures that stack processing only occurs when diagnostics are actually rendered, optimizing performance for rules that may generate many reports. The resulting ts.DiagnosticRelatedInformation[] objects are then attached to the diagnostic, surfacing the stack trace in editors and CLI output.

Advanced Debugging Scenarios

The .at() method enables several critical debugging workflows that distinguish TSSLint from simpler linting frameworks.

Capturing Rule Execution Failures

When a rule throws an exception during execution, the core framework catches the error and uses .at() to preserve the original stack trace. In the execution wrapper found in the core implementation:

try {
  rule(rulesContext);
} catch (err) {
  if (err instanceof Error) {
    report(err.stack ?? err.message, 0, 0).at(err, 0);
  }
}

This pattern ensures that developers see exactly where a rule failed, rather than receiving a generic error message.

Handling Non-Type-Aware Mode Errors

When rules execute without type information and encounter errors, the framework uses a specific technique to capture full diagnostic context:

report(String(err), 0, 0).at(new Error(), Number.MAX_VALUE);

By passing Number.MAX_VALUE as the stackIndex, the core forces getRelatedInformations to include the complete stack trace. This aids in debugging configuration issues or plugin compatibility problems that only surface in specific execution modes.

TSLint Compatibility Layer Integration

The compatibility shim in packages/config/lib/tslint.ts also leverages .at() to handle failures from legacy TSLint rules:

// In packages/config/lib/tslint.ts
report(failureMessage, 0, 0).at(new Error(), Number.MAX_VALUE);

This ensures consistent stack trace handling across both native TSSLint rules and migrated TSLint codebases.

Key Implementation Files

The .at() functionality spans several packages in the monorepo:

Practical Code Examples

Creating Diagnostics with Custom Stack Context

Rule authors can manually create error objects to provide specific debugging context:

export default (ctx: RuleContext) => {
  const { report } = ctx;
  const source = ctx.file.getFullText();
  const evalPos = source.indexOf('eval(');
  
  if (evalPos !== -1) {
    const err = new Error('Avoid eval');
    
    report('Avoid using eval()', evalPos, evalPos + 4)
      .at(err, 0)
      .asError()
      .withFix('Remove eval', () => [{
        span: { start: evalPos, length: 4 },
        newText: '',
      }]);
  }
};

Processing Stack Frames for Display

The getRelatedInformations callback, typically implemented in the CLI worker, transforms captured errors into diagnostic-related information:

function getRelatedInformations(err: Error, stackIndex: number) {
  const frames = err.stack?.split('\n').slice(stackIndex + 1) ?? [];
  return frames.map(frame => ({
    messageText: frame.trim(),
    file: { /* file location extraction logic */ },
    start: 0,
    length: 0,
  }));
}

This function extracts frames starting from the specified index, allowing editors to display a clean breadcrumb trail back to the rule's source.

Summary

  • The .at(err, stackIndex) method modifies Reporter instances to capture error objects and specific stack frame indices
  • Error data is stored as a [Error, number] tuple and processed lazily when relatedInformation is accessed
  • The method enables chaining with other modifiers like asError() and withFix() for fluent diagnostic construction
  • Number.MAX_VALUE as a stackIndex forces inclusion of the complete stack trace for deep debugging scenarios
  • Core implementation resides in packages/core/index.ts with platform-specific getRelatedInformations handling in packages/cli/lib/worker.ts

Frequently Asked Questions

What parameters does the .at() method accept?

The .at() method accepts two parameters: an Error object containing the stack trace to preserve, and a stackIndex number indicating which frame represents the diagnostic entry point. When the stack index is 0, the error's capture point is used; when set to Number.MAX_VALUE, the entire stack trace is included.

How does .at() interact with relatedInformation?

The .at() method stores the error and stack index internally, but the actual ts.DiagnosticRelatedInformation objects are generated lazily. When an IDE or CLI accesses the diagnostic's relatedInformation property, the core invokes the getRelatedInformations callback with the stored error and index, transforming raw stack frames into structured diagnostic objects.

Can .at() be chained with other Reporter methods?

Yes, .at() returns the same Reporter instance, enabling method chaining with other modifiers like asError(), asWarning(), and withFix(). This fluent API allows concise construction of complex diagnostics: report(msg, start, end).at(err, 0).asError().withFix(title, edits).

When should rule authors use Number.MAX_VALUE as the stack index?

Use Number.MAX_VALUE when you need to capture the complete stack trace without filtering specific frames. This is particularly useful when reporting errors from unexpected exceptions where the exact failure point is unknown, or when debugging configuration issues where every frame provides valuable context for tracing the execution path.

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 →