0
# Variable Usage Analysis
1
2
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.
3
4
## Capabilities
5
6
### Variable Usage Collection
7
8
Comprehensive analysis of variable usage throughout a source file.
9
10
```typescript { .api }
11
/**
12
* Collect complete variable usage information for a source file
13
* @param sourceFile - Source file to analyze
14
* @returns Map of identifiers to their usage information
15
*/
16
function collectVariableUsage(sourceFile: ts.SourceFile): Map<ts.Identifier, VariableInfo>;
17
18
/**
19
* Complete information about a variable's declaration and usage
20
*/
21
interface VariableInfo {
22
/** Declaration domain flags (namespace, type, value, import) */
23
domain: DeclarationDomain;
24
/** Whether variable is exported from module */
25
exported: boolean;
26
/** Array of all usage locations */
27
uses: VariableUse[];
28
/** Whether variable is declared in global scope */
29
inGlobalScope: boolean;
30
/** Array of all declaration identifiers */
31
declarations: ts.Identifier[];
32
}
33
34
/**
35
* Information about a specific variable usage
36
*/
37
interface VariableUse {
38
/** Usage domain flags (namespace, type, value, type query) */
39
domain: UsageDomain;
40
/** Identifier node representing the usage */
41
location: ts.Identifier;
42
}
43
```
44
45
### Declaration Domain Analysis
46
47
Utilities for determining what domains a variable declaration occupies.
48
49
```typescript { .api }
50
/**
51
* Get declaration domain for an identifier
52
* @param node - Identifier node to analyze
53
* @returns Declaration domain flags or undefined if not a declaration
54
*/
55
function getDeclarationDomain(node: ts.Identifier): DeclarationDomain | undefined;
56
57
/**
58
* Declaration domain enumeration
59
* Represents the semantic spaces a declaration can occupy
60
*/
61
enum DeclarationDomain {
62
/** Namespace declaration domain */
63
Namespace = 1,
64
/** Type declaration domain */
65
Type = 2,
66
/** Value declaration domain */
67
Value = 4,
68
/** Import declaration domain */
69
Import = 8,
70
/** Any declaration domain (namespace, type, or value) */
71
Any = Namespace | Type | Value
72
}
73
```
74
75
### Usage Domain Analysis
76
77
Utilities for determining how a variable is being used at specific locations.
78
79
```typescript { .api }
80
/**
81
* Get usage domain for an identifier
82
* @param node - Identifier node to analyze
83
* @returns Usage domain flags or undefined if not a usage
84
*/
85
function getUsageDomain(node: ts.Identifier): UsageDomain | undefined;
86
87
/**
88
* Usage domain enumeration
89
* Represents the semantic contexts in which a variable can be used
90
*/
91
enum UsageDomain {
92
/** Used as namespace (Module.member) */
93
Namespace = 1,
94
/** Used as type (: Type, <Type>) */
95
Type = 2,
96
/** Used as value (variable, function call) */
97
Value = 4,
98
/** Used as value or namespace */
99
ValueOrNamespace = Value | Namespace,
100
/** Used in any context */
101
Any = Namespace | Type | Value,
102
/** Used in typeof type query */
103
TypeQuery = 8
104
}
105
```
106
107
**Usage Examples:**
108
109
```typescript
110
import * as ts from "typescript";
111
import {
112
collectVariableUsage,
113
getDeclarationDomain,
114
getUsageDomain,
115
DeclarationDomain,
116
UsageDomain
117
} from "tsutils/util";
118
119
// Comprehensive variable analysis
120
function analyzeVariables(sourceFile: ts.SourceFile) {
121
const variableUsage = collectVariableUsage(sourceFile);
122
123
variableUsage.forEach((info, identifier) => {
124
console.log(`\nVariable: ${identifier.text}`);
125
console.log(` Exported: ${info.exported}`);
126
console.log(` Global scope: ${info.inGlobalScope}`);
127
console.log(` Declarations: ${info.declarations.length}`);
128
console.log(` Uses: ${info.uses.length}`);
129
130
// Analyze declaration domains
131
if (info.domain & DeclarationDomain.Namespace) {
132
console.log(" Declares namespace");
133
}
134
if (info.domain & DeclarationDomain.Type) {
135
console.log(" Declares type");
136
}
137
if (info.domain & DeclarationDomain.Value) {
138
console.log(" Declares value");
139
}
140
if (info.domain & DeclarationDomain.Import) {
141
console.log(" Import declaration");
142
}
143
144
// Analyze usage patterns
145
info.uses.forEach((use, index) => {
146
console.log(` Use ${index + 1}:`);
147
148
if (use.domain & UsageDomain.Namespace) {
149
console.log(" Used as namespace");
150
}
151
if (use.domain & UsageDomain.Type) {
152
console.log(" Used as type");
153
}
154
if (use.domain & UsageDomain.Value) {
155
console.log(" Used as value");
156
}
157
if (use.domain & UsageDomain.TypeQuery) {
158
console.log(" Used in typeof query");
159
}
160
161
console.log(` Location: line ${getLineNumber(sourceFile, use.location)}`);
162
});
163
});
164
}
165
166
// Declaration analysis
167
function analyzeDeclarations(sourceFile: ts.SourceFile) {
168
sourceFile.forEachChild(function visit(node) {
169
if (ts.isIdentifier(node)) {
170
const declarationDomain = getDeclarationDomain(node);
171
172
if (declarationDomain !== undefined) {
173
console.log(`Declaration "${node.text}"`);
174
175
if (declarationDomain & DeclarationDomain.Namespace) {
176
console.log(" Creates namespace");
177
}
178
if (declarationDomain & DeclarationDomain.Type) {
179
console.log(" Creates type");
180
}
181
if (declarationDomain & DeclarationDomain.Value) {
182
console.log(" Creates value");
183
}
184
}
185
}
186
187
node.forEachChild(visit);
188
});
189
}
190
191
// Usage pattern analysis
192
function analyzeUsagePatterns(sourceFile: ts.SourceFile) {
193
const typeUsages: ts.Identifier[] = [];
194
const valueUsages: ts.Identifier[] = [];
195
const namespaceUsages: ts.Identifier[] = [];
196
197
sourceFile.forEachChild(function visit(node) {
198
if (ts.isIdentifier(node)) {
199
const usageDomain = getUsageDomain(node);
200
201
if (usageDomain !== undefined) {
202
if (usageDomain & UsageDomain.Type) {
203
typeUsages.push(node);
204
}
205
if (usageDomain & UsageDomain.Value) {
206
valueUsages.push(node);
207
}
208
if (usageDomain & UsageDomain.Namespace) {
209
namespaceUsages.push(node);
210
}
211
}
212
}
213
214
node.forEachChild(visit);
215
});
216
217
console.log(`Type usages: ${typeUsages.length}`);
218
console.log(`Value usages: ${valueUsages.length}`);
219
console.log(`Namespace usages: ${namespaceUsages.length}`);
220
}
221
222
// Export analysis
223
function analyzeExports(sourceFile: ts.SourceFile) {
224
const variableUsage = collectVariableUsage(sourceFile);
225
const exportedVariables: string[] = [];
226
227
variableUsage.forEach((info, identifier) => {
228
if (info.exported) {
229
exportedVariables.push(identifier.text);
230
}
231
});
232
233
console.log("Exported variables:", exportedVariables);
234
}
235
236
// Scope analysis
237
function analyzeScopeLevel(sourceFile: ts.SourceFile) {
238
const variableUsage = collectVariableUsage(sourceFile);
239
const globalVariables: string[] = [];
240
const localVariables: string[] = [];
241
242
variableUsage.forEach((info, identifier) => {
243
if (info.inGlobalScope) {
244
globalVariables.push(identifier.text);
245
} else {
246
localVariables.push(identifier.text);
247
}
248
});
249
250
console.log(`Global variables: ${globalVariables.length}`);
251
console.log(`Local variables: ${localVariables.length}`);
252
}
253
254
// Cross-reference analysis
255
function analyzeCrossReferences(sourceFile: ts.SourceFile) {
256
const variableUsage = collectVariableUsage(sourceFile);
257
258
variableUsage.forEach((info, identifier) => {
259
if (info.uses.length === 0) {
260
console.log(`Unused variable: ${identifier.text}`);
261
} else if (info.declarations.length > 1) {
262
console.log(`Variable "${identifier.text}" has multiple declarations`);
263
}
264
265
// Check for mixed usage domains
266
const usageDomains = new Set(info.uses.map(use => use.domain));
267
if (usageDomains.size > 1) {
268
console.log(`Variable "${identifier.text}" used in multiple contexts`);
269
}
270
});
271
}
272
273
// Helper function for line number calculation
274
function getLineNumber(sourceFile: ts.SourceFile, node: ts.Node): number {
275
return sourceFile.getLineAndCharacterOfPosition(node.start).line + 1;
276
}
277
```
278
279
**Common Use Cases:**
280
281
1. **Unused Variable Detection**: Identify variables that are declared but never used
282
2. **Export Analysis**: Find all exported variables and their usage patterns
283
3. **Scope Analysis**: Determine variable scope levels and potential conflicts
284
4. **Cross-Reference Generation**: Build symbol tables and reference maps
285
5. **Refactoring Support**: Understand variable dependencies for safe refactoring
286
6. **Type vs Value Usage**: Distinguish between type-level and value-level usage of identifiers
287
7. **Import Analysis**: Track imported symbols and their usage throughout the file
288
8. **Declaration Merging**: Identify variables with multiple declarations (interfaces, namespaces)
289
290
The variable usage analysis utilities are particularly valuable for:
291
- Code analysis tools and linters
292
- Refactoring engines
293
- IDE features like "find all references"
294
- Dead code elimination
295
- Symbol navigation and intellisense
296
- Type checking and validation