Utility functions for working with TypeScript's Abstract Syntax Tree (AST) and type system
—
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.
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;
}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
}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:
The variable usage analysis utilities are particularly valuable for:
Install with Tessl CLI
npx tessl i tessl/npm-tsutils