CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-typescript-eslint--scope-manager

TypeScript scope analyser for ESLint that provides comprehensive scope analysis capabilities for JavaScript and TypeScript code

Pending
Overview
Eval results
Files

variable-system.mddocs/

Variable System

The variable tracking system provides TypeScript-aware type context analysis and comprehensive definition tracking for all identifiers in analyzed code.

Capabilities

Variable Class

The main Variable class that represents variables with TypeScript type context awareness.

/**
 * A Variable represents a locally scoped identifier. These include arguments to functions.
 */
class Variable extends VariableBase {
  /** Unique identifier for this variable instance */
  readonly $id: number;
  
  /** The variable name, as given in the source code */
  readonly name: string;
  
  /** The array of the definitions of this variable */
  readonly defs: Definition[];
  
  /** The array of Identifier nodes which define this variable */
  readonly identifiers: TSESTree.Identifier[];
  
  /** List of References of this variable in its defining scope and all nested scopes */
  readonly references: Reference[];
  
  /** The scope where this variable is defined */
  readonly scope: Scope;
  
  /** True if the variable is considered used for the purposes of no-unused-vars */
  eslintUsed: boolean;
  
  /** True if the variable is valid in a type context, false otherwise */
  get isTypeVariable(): boolean;
  
  /** True if the variable is valid in a value context, false otherwise */
  get isValueVariable(): boolean;
}

ESLint Compatible Variable

ESLint-compatible variable implementation for seamless integration with existing ESLint rules.

/**
 * ESLint-compatible variable that maintains compatibility with standard ESLint scope analysis
 */
class ESLintScopeVariable extends VariableBase {
  readonly $id: number;
  readonly name: string;
  readonly defs: Definition[];
  readonly identifiers: TSESTree.Identifier[];
  readonly references: Reference[];
  readonly scope: Scope;
  eslintUsed: boolean;
  
  /** If this key exists, this variable is a global variable added by ESLint */
  writeable?: boolean;
  
  /** Indicates if there are globals comment directives */
  eslintExplicitGlobal?: boolean;
  
  /** The configured value in config files */
  eslintImplicitGlobalSetting?: 'readonly' | 'writable';
  
  /** Global variable comments from source code */
  eslintExplicitGlobalComments?: TSESTree.Comment[];
}

Implicit Library Variable

Variables automatically defined by TypeScript library definitions.

/**
 * Variable representing TypeScript library definitions (e.g., console, window, etc.)
 */
class ImplicitLibVariable extends VariableBase {
  readonly $id: number;
  readonly name: string;
  readonly defs: LibDefinition[];
  readonly identifiers: TSESTree.Identifier[];
  readonly references: Reference[];
  readonly scope: Scope;
  eslintUsed: boolean;
  
  constructor(scope: Scope, name: string, options: ImplicitLibVariableOptions);
}

interface ImplicitLibVariableOptions {
  readonly eslintImplicitGlobalSetting?: 'readonly' | 'writable';
  readonly isTypeVariable?: boolean;
  readonly isValueVariable?: boolean;
  readonly writeable?: boolean;
}

interface LibDefinition {
  libs: readonly LibDefinition[];
  variables: readonly [string, ImplicitLibVariableOptions][];
}

Variable Union Type

Union type representing all possible variable types in the system.

type ScopeVariable = ESLintScopeVariable | Variable;

Usage Examples:

import { analyze } from "@typescript-eslint/scope-manager";
import { parse } from "@typescript-eslint/parser";

const code = `
interface User {
  name: string;
  age: number;
}

function createUser(name: string, age: number): User {
  const user: User = { name, age };
  return user;
}

enum Status {
  Active,
  Inactive
}
`;

const ast = parse(code);
const scopeManager = analyze(ast, { 
  sourceType: 'module',
  lib: ['es2020', 'dom']
});

// Analyze all variables
scopeManager.variables.forEach(variable => {
  console.log(`Variable: ${variable.name}`);
  console.log(`  Type context: ${variable.isTypeVariable}`);
  console.log(`  Value context: ${variable.isValueVariable}`);
  console.log(`  Scope: ${variable.scope.type}`);
  console.log(`  Definitions: ${variable.defs.length}`);
  console.log(`  References: ${variable.references.length}`);
  console.log(`  ESLint used: ${variable.eslintUsed}`);
});

// Check type vs value variables
const typeOnlyVars = scopeManager.variables.filter(v => 
  v.isTypeVariable && !v.isValueVariable
);
console.log('Type-only variables:', typeOnlyVars.map(v => v.name));

const valueOnlyVars = scopeManager.variables.filter(v => 
  v.isValueVariable && !v.isTypeVariable  
);
console.log('Value-only variables:', valueOnlyVars.map(v => v.name));

const dualContextVars = scopeManager.variables.filter(v =>
  v.isTypeVariable && v.isValueVariable
);
console.log('Dual-context variables:', dualContextVars.map(v => v.name));

Variable Context Analysis

TypeScript variables can exist in different contexts:

// Examples of different variable contexts

// Type-only context
interface Point { x: number; y: number } // 'Point' is type-only
type StringOrNumber = string | number;    // 'StringOrNumber' is type-only

// Value-only context  
const message = "Hello";                  // 'message' is value-only
function greet() { }                      // 'greet' is value-only

// Dual context (both type and value)
class Rectangle {                         // 'Rectangle' is both type and value
  width: number;
  height: number;
}

enum Color {                              // 'Color' is both type and value
  Red, Green, Blue
}

namespace Utils {                         // 'Utils' is both type and value
  export const helper = () => {};
}

// Usage analysis
const scopeManager = analyze(ast);
scopeManager.variables.forEach(variable => {
  if (variable.isTypeVariable && variable.isValueVariable) {
    console.log(`${variable.name} can be used as both type and value`);
  } else if (variable.isTypeVariable) {
    console.log(`${variable.name} can only be used as a type`);
  } else if (variable.isValueVariable) {
    console.log(`${variable.name} can only be used as a value`);
  }
});

Variable Definition Analysis

Variables can have multiple definitions from various sources:

// Analyze variable definitions
scopeManager.variables.forEach(variable => {
  console.log(`\nVariable: ${variable.name}`);
  
  variable.defs.forEach((def, index) => {
    console.log(`  Definition ${index}:`);
    console.log(`    Type: ${def.type}`);
    console.log(`    Node: ${def.node.type}`);
    console.log(`    Is type definition: ${def.isTypeDefinition}`);
    console.log(`    Is variable definition: ${def.isVariableDefinition}`);
  });
  
  // Check if variable is redeclared
  if (variable.defs.length > 1) {
    console.log(`  ⚠️  Variable ${variable.name} is redeclared`);
  }
  
  // Analyze identifiers
  console.log(`  Identifier nodes: ${variable.identifiers.length}`);
  variable.identifiers.forEach((id, index) => {
    console.log(`    ${index}: ${id.name} at line ${id.loc?.start.line}`);
  });
});

Variable Reference Analysis

Track how variables are used throughout the code:

// Analyze variable references
scopeManager.variables.forEach(variable => {
  console.log(`\nVariable: ${variable.name}`);
  console.log(`References: ${variable.references.length}`);
  
  variable.references.forEach((ref, index) => {
    console.log(`  Reference ${index}:`);
    console.log(`    From scope: ${ref.from.type}`);
    console.log(`    Is read: ${ref.isRead()}`);
    console.log(`    Is write: ${ref.isWrite()}`);
    console.log(`    Resolved: ${ref.resolved ? 'Yes' : 'No'}`);
    
    if (ref.writeExpr) {
      console.log(`    Write expression: ${ref.writeExpr.type}`);
    }
  });
  
  // Analyze usage patterns
  const reads = variable.references.filter(ref => ref.isRead());
  const writes = variable.references.filter(ref => ref.isWrite());
  
  console.log(`  Read operations: ${reads.length}`);
  console.log(`  Write operations: ${writes.length}`);
  
  if (writes.length === 0 && variable.defs.length > 0) {
    console.log(`  ⚠️  Variable ${variable.name} is never modified`);
  }
  
  if (reads.length === 0) {
    console.log(`  ⚠️  Variable ${variable.name} is never read`);
  }
});

Implicit Library Variables

TypeScript library definitions create implicit variables:

// Analyze implicit library variables  
const globalScope = scopeManager.globalScope;
if (globalScope) {
  const implicitVars = globalScope.variables.filter(v => 
    v instanceof ImplicitLibVariable
  );
  
  console.log('Implicit library variables:');
  implicitVars.forEach(variable => {
    const implicitVar = variable as ImplicitLibVariable;
    console.log(`  ${implicitVar.name}`);
    console.log(`    Type variable: ${implicitVar.isTypeVariable}`);
    console.log(`    Value variable: ${implicitVar.isValueVariable}`);
    console.log(`    Writable: ${implicitVar.writeable}`);
  });
}

// Common implicit variables from TypeScript libs:
// - console (from lib.dom.d.ts or lib.node.d.ts)
// - window, document (from lib.dom.d.ts)
// - process, Buffer (from @types/node)
// - Promise, Array, Object (from lib.es*.d.ts)

Variable Scope Relationships

Understanding how variables relate to their scopes:

// Analyze variable-scope relationships
scopeManager.scopes.forEach(scope => {
  console.log(`\nScope: ${scope.type}`);
  console.log(`Variables defined in this scope: ${scope.set.size}`);
  
  // Variables defined directly in this scope
  scope.set.forEach((variable, name) => {
    console.log(`  Owns: ${name}`);
  });
  
  // All variables accessible in this scope
  console.log(`Total accessible variables: ${scope.variables.length}`);
  
  // Check variable scope chain
  scope.variables.forEach(variable => {
    if (variable.scope === scope) {
      console.log(`  Local: ${variable.name}`);
    } else {
      console.log(`  Inherited: ${variable.name} (from ${variable.scope.type})`);
    }
  });
});

Install with Tessl CLI

npx tessl i tessl/npm-typescript-eslint--scope-manager

docs

analysis.md

definition-system.md

index.md

reference-system.md

scope-management.md

scope-types.md

variable-system.md

tile.json