CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tsutils

Utility functions for working with TypeScript's Abstract Syntax Tree (AST) and type system

Pending
Overview
Eval results
Files

variable-usage.mddocs/

Variable Usage Analysis

Specialized utilities for analyzing variable declarations, usage patterns, and scope analysis. These functions provide detailed information about variable domains, export status, usage locations, and declaration contexts within TypeScript source files.

Capabilities

Variable Usage Collection

Comprehensive analysis of variable usage throughout a source file.

/**
 * Collect complete variable usage information for a source file
 * @param sourceFile - Source file to analyze
 * @returns Map of identifiers to their usage information
 */
function collectVariableUsage(sourceFile: ts.SourceFile): Map<ts.Identifier, VariableInfo>;

/**
 * Complete information about a variable's declaration and usage
 */
interface VariableInfo {
  /** Declaration domain flags (namespace, type, value, import) */
  domain: DeclarationDomain;
  /** Whether variable is exported from module */
  exported: boolean;
  /** Array of all usage locations */
  uses: VariableUse[];
  /** Whether variable is declared in global scope */
  inGlobalScope: boolean;
  /** Array of all declaration identifiers */
  declarations: ts.Identifier[];
}

/**
 * Information about a specific variable usage
 */
interface VariableUse {
  /** Usage domain flags (namespace, type, value, type query) */
  domain: UsageDomain;
  /** Identifier node representing the usage */
  location: ts.Identifier;
}

Declaration Domain Analysis

Utilities for determining what domains a variable declaration occupies.

/**
 * Get declaration domain for an identifier
 * @param node - Identifier node to analyze
 * @returns Declaration domain flags or undefined if not a declaration
 */
function getDeclarationDomain(node: ts.Identifier): DeclarationDomain | undefined;

/**
 * Declaration domain enumeration
 * Represents the semantic spaces a declaration can occupy
 */
enum DeclarationDomain {
  /** Namespace declaration domain */
  Namespace = 1,
  /** Type declaration domain */
  Type = 2,
  /** Value declaration domain */
  Value = 4,
  /** Import declaration domain */
  Import = 8,
  /** Any declaration domain (namespace, type, or value) */
  Any = Namespace | Type | Value
}

Usage Domain Analysis

Utilities for determining how a variable is being used at specific locations.

/**
 * Get usage domain for an identifier
 * @param node - Identifier node to analyze
 * @returns Usage domain flags or undefined if not a usage
 */
function getUsageDomain(node: ts.Identifier): UsageDomain | undefined;

/**
 * Usage domain enumeration
 * Represents the semantic contexts in which a variable can be used
 */
enum UsageDomain {
  /** Used as namespace (Module.member) */
  Namespace = 1,
  /** Used as type (: Type, <Type>) */
  Type = 2,
  /** Used as value (variable, function call) */
  Value = 4,
  /** Used as value or namespace */
  ValueOrNamespace = Value | Namespace,
  /** Used in any context */
  Any = Namespace | Type | Value,
  /** Used in typeof type query */
  TypeQuery = 8
}

Usage Examples:

import * as ts from "typescript";
import { 
  collectVariableUsage, 
  getDeclarationDomain, 
  getUsageDomain,
  DeclarationDomain,
  UsageDomain
} from "tsutils/util";

// Comprehensive variable analysis
function analyzeVariables(sourceFile: ts.SourceFile) {
  const variableUsage = collectVariableUsage(sourceFile);
  
  variableUsage.forEach((info, identifier) => {
    console.log(`\nVariable: ${identifier.text}`);
    console.log(`  Exported: ${info.exported}`);
    console.log(`  Global scope: ${info.inGlobalScope}`);
    console.log(`  Declarations: ${info.declarations.length}`);
    console.log(`  Uses: ${info.uses.length}`);
    
    // Analyze declaration domains
    if (info.domain & DeclarationDomain.Namespace) {
      console.log("  Declares namespace");
    }
    if (info.domain & DeclarationDomain.Type) {
      console.log("  Declares type");
    }
    if (info.domain & DeclarationDomain.Value) {
      console.log("  Declares value");
    }
    if (info.domain & DeclarationDomain.Import) {
      console.log("  Import declaration");
    }
    
    // Analyze usage patterns
    info.uses.forEach((use, index) => {
      console.log(`  Use ${index + 1}:`);
      
      if (use.domain & UsageDomain.Namespace) {
        console.log("    Used as namespace");
      }
      if (use.domain & UsageDomain.Type) {
        console.log("    Used as type");
      }
      if (use.domain & UsageDomain.Value) {
        console.log("    Used as value");
      }
      if (use.domain & UsageDomain.TypeQuery) {
        console.log("    Used in typeof query");
      }
      
      console.log(`    Location: line ${getLineNumber(sourceFile, use.location)}`);
    });
  });
}

// Declaration analysis
function analyzeDeclarations(sourceFile: ts.SourceFile) {
  sourceFile.forEachChild(function visit(node) {
    if (ts.isIdentifier(node)) {
      const declarationDomain = getDeclarationDomain(node);
      
      if (declarationDomain !== undefined) {
        console.log(`Declaration "${node.text}"`);
        
        if (declarationDomain & DeclarationDomain.Namespace) {
          console.log("  Creates namespace");
        }
        if (declarationDomain & DeclarationDomain.Type) {
          console.log("  Creates type");
        }
        if (declarationDomain & DeclarationDomain.Value) {
          console.log("  Creates value");
        }
      }
    }
    
    node.forEachChild(visit);
  });
}

// Usage pattern analysis
function analyzeUsagePatterns(sourceFile: ts.SourceFile) {
  const typeUsages: ts.Identifier[] = [];
  const valueUsages: ts.Identifier[] = [];
  const namespaceUsages: ts.Identifier[] = [];
  
  sourceFile.forEachChild(function visit(node) {
    if (ts.isIdentifier(node)) {
      const usageDomain = getUsageDomain(node);
      
      if (usageDomain !== undefined) {
        if (usageDomain & UsageDomain.Type) {
          typeUsages.push(node);
        }
        if (usageDomain & UsageDomain.Value) {
          valueUsages.push(node);
        }
        if (usageDomain & UsageDomain.Namespace) {
          namespaceUsages.push(node);
        }
      }
    }
    
    node.forEachChild(visit);
  });
  
  console.log(`Type usages: ${typeUsages.length}`);
  console.log(`Value usages: ${valueUsages.length}`);
  console.log(`Namespace usages: ${namespaceUsages.length}`);
}

// Export analysis
function analyzeExports(sourceFile: ts.SourceFile) {
  const variableUsage = collectVariableUsage(sourceFile);
  const exportedVariables: string[] = [];
  
  variableUsage.forEach((info, identifier) => {
    if (info.exported) {
      exportedVariables.push(identifier.text);
    }
  });
  
  console.log("Exported variables:", exportedVariables);
}

// Scope analysis
function analyzeScopeLevel(sourceFile: ts.SourceFile) {
  const variableUsage = collectVariableUsage(sourceFile);
  const globalVariables: string[] = [];
  const localVariables: string[] = [];
  
  variableUsage.forEach((info, identifier) => {
    if (info.inGlobalScope) {
      globalVariables.push(identifier.text);
    } else {
      localVariables.push(identifier.text);
    }
  });
  
  console.log(`Global variables: ${globalVariables.length}`);
  console.log(`Local variables: ${localVariables.length}`);
}

// Cross-reference analysis
function analyzeCrossReferences(sourceFile: ts.SourceFile) {
  const variableUsage = collectVariableUsage(sourceFile);
  
  variableUsage.forEach((info, identifier) => {
    if (info.uses.length === 0) {
      console.log(`Unused variable: ${identifier.text}`);
    } else if (info.declarations.length > 1) {
      console.log(`Variable "${identifier.text}" has multiple declarations`);
    }
    
    // Check for mixed usage domains
    const usageDomains = new Set(info.uses.map(use => use.domain));
    if (usageDomains.size > 1) {
      console.log(`Variable "${identifier.text}" used in multiple contexts`);
    }
  });
}

// Helper function for line number calculation
function getLineNumber(sourceFile: ts.SourceFile, node: ts.Node): number {
  return sourceFile.getLineAndCharacterOfPosition(node.start).line + 1;
}

Common Use Cases:

  1. Unused Variable Detection: Identify variables that are declared but never used
  2. Export Analysis: Find all exported variables and their usage patterns
  3. Scope Analysis: Determine variable scope levels and potential conflicts
  4. Cross-Reference Generation: Build symbol tables and reference maps
  5. Refactoring Support: Understand variable dependencies for safe refactoring
  6. Type vs Value Usage: Distinguish between type-level and value-level usage of identifiers
  7. Import Analysis: Track imported symbols and their usage throughout the file
  8. Declaration Merging: Identify variables with multiple declarations (interfaces, namespaces)

The variable usage analysis utilities are particularly valuable for:

  • Code analysis tools and linters
  • Refactoring engines
  • IDE features like "find all references"
  • Dead code elimination
  • Symbol navigation and intellisense
  • Type checking and validation

Install with Tessl CLI

npx tessl i tessl/npm-tsutils

docs

ast-traversal.md

code-analysis.md

control-flow.md

index.md

node-typeguards.md

text-processing.md

type-guards.md

type-utilities.md

variable-usage.md

tile.json