How to Use withoutCache() to Opt Specific Diagnostics Out of Caching in TSSLint

Call .withoutCache() on the Reporter object returned by context.report() to prevent a specific diagnostic from being stored in TSSLint's FileLintCache, ensuring the diagnostic is re-evaluated on every lint run.

TSSLint is a TypeScript linter that caches diagnostics in memory to speed up incremental linting. However, some diagnostics depend on volatile data or require fresh evaluation every time. The withoutCache() method, available on the Reporter object in johnsoncodehk/tsslint, allows you to opt specific diagnostics out of this caching mechanism.

What Is withoutCache() and How Does It Work?

When you call context.report() in a TSSLint rule, it returns a Reporter object. This object provides chainable methods to modify the diagnostic, including .withoutCache().

According to the source code in packages/core/index.ts (lines 281-292), TSSLint maintains an in-memory FileLintCache that stores diagnostics between incremental lint runs. When you report a diagnostic, it is automatically pushed into cache[1][ruleId][1]. The .withoutCache() method looks up this cache entry and splices the diagnostic out of the array, ensuring it is not persisted for future runs.

When to Use withoutCache() in TSSLint Rules

You should use .withoutCache() when a diagnostic's validity depends on data that may change between lint runs, or when you explicitly need fresh evaluation every time.

  • Runtime-only dependencies: Diagnostics based on environment variables, file system state, or external API responses that change independently of source code.
  • Expensive conditional rules: When a rule is costly to recompute only under specific conditions, but you want the result fresh when those conditions apply.
  • Debug or experimental rules: Temporary diagnostics that should not pollute the production cache or affect incremental linting performance.
  • Evolving metrics: Rules that track file-wide statistics which change based on factors outside the AST, requiring re-evaluation on every lint.

If no FileLintCache is supplied (for example, when lint() is called without a cache object), the .withoutCache() call has no effect and acts as a no-op.

How to Implement withoutCache() in Your Rules

Basic Usage

Call .withoutCache() on the Reporter object returned by context.report():

export const noConsoleRule = (ctx: RuleContext) => {
  const { report } = ctx;
  const source = ctx.file.getFullText();
  
  const consoleRegex = /\bconsole\./g;
  let match: RegExpExecArray | null;
  
  while ((match = consoleRegex.exec(source))) {
    report('Avoid using console statements', match.index, match.index + match[0].length)
      .asWarning()
      .withoutCache();  // Prevents this diagnostic from being cached
  }
};

Chaining with Other Modifiers

The .withoutCache() method returns the same Reporter instance, allowing you to chain it with other modifiers like .asError() or .withFix():

report('Unexpected use of eval()', start, end)
  .asError()
  .withFix('Remove eval', () => [{ 
    span: { start, length: end - start }, 
    newText: '' 
  }])
  .withoutCache();  // Skip caching while still offering a fix

Conditional Opt-Out

You can conditionally apply .withoutCache() based on runtime conditions:

if (process.env.NODE_ENV === 'development') {
  // In dev mode, always re-evaluate this diagnostic
  report('Debug log detected', start, end).withoutCache();
} else {
  report('Debug log detected', start, end);
}

Understanding the Internal Implementation

The .withoutCache() method is defined in packages/types/index.ts (lines 55-60) as part of the Reporter interface. The actual implementation resides in packages/core/index.ts within the createLinter function (lines 281-292).

When a diagnostic is reported, the system creates a temporary cachedObj reference. If a FileLintCache exists, the diagnostic is pushed into cache[1][ruleId][1]. The .withoutCache() implementation checks for this cachedObj, locates the specific rule's cache array, and uses splice() to remove the diagnostic entry.

This mechanism ensures that the diagnostic remains in the current lint results (it has already been emitted to the result set) but will not be available for reuse in subsequent incremental lint passes. The CLI worker in packages/cli/lib/worker.ts creates and passes the FileLintCache to the core linter, while packages/cli/lib/cache.ts handles serialization between runs.

Summary

  • .withoutCache() prevents specific diagnostics from being stored in TSSLint's FileLintCache, forcing re-evaluation on every lint run.
  • Use it for diagnostics dependent on runtime data, environment variables, or external state that changes independently of source code.
  • The method is chainable with other Reporter modifiers like .asError() and .withFix().
  • Internally, it splices the diagnostic out of the rule-specific cache array in packages/core/index.ts (lines 281-292).
  • If no cache is supplied (e.g., non-incremental runs), the call is a no-op.

Frequently Asked Questions

Does withoutCache() affect the diagnostic output or only the caching?

.withoutCache() only affects the caching mechanism. The diagnostic is still emitted to the lint results and will appear in the output exactly as if caching were not used. The method simply removes the diagnostic from the FileLintCache after it has been reported, ensuring it is not reused in subsequent incremental lint passes.

Can I use withoutCache() when running TSSLint without incremental caching?

Yes, but it has no effect. If you call lint() without supplying a FileLintCache (for example, in a single-run CLI invocation or custom integration), the .withoutCache() method becomes a no-op. It checks for the existence of a cache object before attempting to splice the diagnostic, so it is safe to use unconditionally in rules that may run in both cached and non-cached environments.

How does withoutCache() interact with other Reporter chain methods?

.withoutCache() returns the same Reporter instance, allowing full chaining. You can call it before or after other modifiers like .asError(), .asWarning(), or .withFix(). For example, report('Message', 0, 10).asError().withoutCache().withFix('Fix', fixFn) works correctly. The order does not affect the final diagnostic output, only the caching behavior.

Where is the withoutCache() method defined in the TSSLint source code?

The method signature is defined in packages/types/index.ts (lines 55-60) as part of the Reporter interface, and implemented in packages/core/index.ts (lines 281-292) within the createLinter function. The implementation handles the logic for splicing diagnostics out of the rule-specific cache array when a FileLintCache is present.

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 →