0
# Variable System
1
2
The variable tracking system provides TypeScript-aware type context analysis and comprehensive definition tracking for all identifiers in analyzed code.
3
4
## Capabilities
5
6
### Variable Class
7
8
The main Variable class that represents variables with TypeScript type context awareness.
9
10
```typescript { .api }
11
/**
12
* A Variable represents a locally scoped identifier. These include arguments to functions.
13
*/
14
class Variable extends VariableBase {
15
/** Unique identifier for this variable instance */
16
readonly $id: number;
17
18
/** The variable name, as given in the source code */
19
readonly name: string;
20
21
/** The array of the definitions of this variable */
22
readonly defs: Definition[];
23
24
/** The array of Identifier nodes which define this variable */
25
readonly identifiers: TSESTree.Identifier[];
26
27
/** List of References of this variable in its defining scope and all nested scopes */
28
readonly references: Reference[];
29
30
/** The scope where this variable is defined */
31
readonly scope: Scope;
32
33
/** True if the variable is considered used for the purposes of no-unused-vars */
34
eslintUsed: boolean;
35
36
/** True if the variable is valid in a type context, false otherwise */
37
get isTypeVariable(): boolean;
38
39
/** True if the variable is valid in a value context, false otherwise */
40
get isValueVariable(): boolean;
41
}
42
```
43
44
### ESLint Compatible Variable
45
46
ESLint-compatible variable implementation for seamless integration with existing ESLint rules.
47
48
```typescript { .api }
49
/**
50
* ESLint-compatible variable that maintains compatibility with standard ESLint scope analysis
51
*/
52
class ESLintScopeVariable extends VariableBase {
53
readonly $id: number;
54
readonly name: string;
55
readonly defs: Definition[];
56
readonly identifiers: TSESTree.Identifier[];
57
readonly references: Reference[];
58
readonly scope: Scope;
59
eslintUsed: boolean;
60
61
/** If this key exists, this variable is a global variable added by ESLint */
62
writeable?: boolean;
63
64
/** Indicates if there are globals comment directives */
65
eslintExplicitGlobal?: boolean;
66
67
/** The configured value in config files */
68
eslintImplicitGlobalSetting?: 'readonly' | 'writable';
69
70
/** Global variable comments from source code */
71
eslintExplicitGlobalComments?: TSESTree.Comment[];
72
}
73
```
74
75
### Implicit Library Variable
76
77
Variables automatically defined by TypeScript library definitions.
78
79
```typescript { .api }
80
/**
81
* Variable representing TypeScript library definitions (e.g., console, window, etc.)
82
*/
83
class ImplicitLibVariable extends VariableBase {
84
readonly $id: number;
85
readonly name: string;
86
readonly defs: LibDefinition[];
87
readonly identifiers: TSESTree.Identifier[];
88
readonly references: Reference[];
89
readonly scope: Scope;
90
eslintUsed: boolean;
91
92
constructor(scope: Scope, name: string, options: ImplicitLibVariableOptions);
93
}
94
95
interface ImplicitLibVariableOptions {
96
readonly eslintImplicitGlobalSetting?: 'readonly' | 'writable';
97
readonly isTypeVariable?: boolean;
98
readonly isValueVariable?: boolean;
99
readonly writeable?: boolean;
100
}
101
102
interface LibDefinition {
103
libs: readonly LibDefinition[];
104
variables: readonly [string, ImplicitLibVariableOptions][];
105
}
106
```
107
108
### Variable Union Type
109
110
Union type representing all possible variable types in the system.
111
112
```typescript { .api }
113
type ScopeVariable = ESLintScopeVariable | Variable;
114
```
115
116
**Usage Examples:**
117
118
```typescript
119
import { analyze } from "@typescript-eslint/scope-manager";
120
import { parse } from "@typescript-eslint/parser";
121
122
const code = `
123
interface User {
124
name: string;
125
age: number;
126
}
127
128
function createUser(name: string, age: number): User {
129
const user: User = { name, age };
130
return user;
131
}
132
133
enum Status {
134
Active,
135
Inactive
136
}
137
`;
138
139
const ast = parse(code);
140
const scopeManager = analyze(ast, {
141
sourceType: 'module',
142
lib: ['es2020', 'dom']
143
});
144
145
// Analyze all variables
146
scopeManager.variables.forEach(variable => {
147
console.log(`Variable: ${variable.name}`);
148
console.log(` Type context: ${variable.isTypeVariable}`);
149
console.log(` Value context: ${variable.isValueVariable}`);
150
console.log(` Scope: ${variable.scope.type}`);
151
console.log(` Definitions: ${variable.defs.length}`);
152
console.log(` References: ${variable.references.length}`);
153
console.log(` ESLint used: ${variable.eslintUsed}`);
154
});
155
156
// Check type vs value variables
157
const typeOnlyVars = scopeManager.variables.filter(v =>
158
v.isTypeVariable && !v.isValueVariable
159
);
160
console.log('Type-only variables:', typeOnlyVars.map(v => v.name));
161
162
const valueOnlyVars = scopeManager.variables.filter(v =>
163
v.isValueVariable && !v.isTypeVariable
164
);
165
console.log('Value-only variables:', valueOnlyVars.map(v => v.name));
166
167
const dualContextVars = scopeManager.variables.filter(v =>
168
v.isTypeVariable && v.isValueVariable
169
);
170
console.log('Dual-context variables:', dualContextVars.map(v => v.name));
171
```
172
173
### Variable Context Analysis
174
175
TypeScript variables can exist in different contexts:
176
177
```typescript
178
// Examples of different variable contexts
179
180
// Type-only context
181
interface Point { x: number; y: number } // 'Point' is type-only
182
type StringOrNumber = string | number; // 'StringOrNumber' is type-only
183
184
// Value-only context
185
const message = "Hello"; // 'message' is value-only
186
function greet() { } // 'greet' is value-only
187
188
// Dual context (both type and value)
189
class Rectangle { // 'Rectangle' is both type and value
190
width: number;
191
height: number;
192
}
193
194
enum Color { // 'Color' is both type and value
195
Red, Green, Blue
196
}
197
198
namespace Utils { // 'Utils' is both type and value
199
export const helper = () => {};
200
}
201
202
// Usage analysis
203
const scopeManager = analyze(ast);
204
scopeManager.variables.forEach(variable => {
205
if (variable.isTypeVariable && variable.isValueVariable) {
206
console.log(`${variable.name} can be used as both type and value`);
207
} else if (variable.isTypeVariable) {
208
console.log(`${variable.name} can only be used as a type`);
209
} else if (variable.isValueVariable) {
210
console.log(`${variable.name} can only be used as a value`);
211
}
212
});
213
```
214
215
### Variable Definition Analysis
216
217
Variables can have multiple definitions from various sources:
218
219
```typescript
220
// Analyze variable definitions
221
scopeManager.variables.forEach(variable => {
222
console.log(`\nVariable: ${variable.name}`);
223
224
variable.defs.forEach((def, index) => {
225
console.log(` Definition ${index}:`);
226
console.log(` Type: ${def.type}`);
227
console.log(` Node: ${def.node.type}`);
228
console.log(` Is type definition: ${def.isTypeDefinition}`);
229
console.log(` Is variable definition: ${def.isVariableDefinition}`);
230
});
231
232
// Check if variable is redeclared
233
if (variable.defs.length > 1) {
234
console.log(` ⚠️ Variable ${variable.name} is redeclared`);
235
}
236
237
// Analyze identifiers
238
console.log(` Identifier nodes: ${variable.identifiers.length}`);
239
variable.identifiers.forEach((id, index) => {
240
console.log(` ${index}: ${id.name} at line ${id.loc?.start.line}`);
241
});
242
});
243
```
244
245
### Variable Reference Analysis
246
247
Track how variables are used throughout the code:
248
249
```typescript
250
// Analyze variable references
251
scopeManager.variables.forEach(variable => {
252
console.log(`\nVariable: ${variable.name}`);
253
console.log(`References: ${variable.references.length}`);
254
255
variable.references.forEach((ref, index) => {
256
console.log(` Reference ${index}:`);
257
console.log(` From scope: ${ref.from.type}`);
258
console.log(` Is read: ${ref.isRead()}`);
259
console.log(` Is write: ${ref.isWrite()}`);
260
console.log(` Resolved: ${ref.resolved ? 'Yes' : 'No'}`);
261
262
if (ref.writeExpr) {
263
console.log(` Write expression: ${ref.writeExpr.type}`);
264
}
265
});
266
267
// Analyze usage patterns
268
const reads = variable.references.filter(ref => ref.isRead());
269
const writes = variable.references.filter(ref => ref.isWrite());
270
271
console.log(` Read operations: ${reads.length}`);
272
console.log(` Write operations: ${writes.length}`);
273
274
if (writes.length === 0 && variable.defs.length > 0) {
275
console.log(` ⚠️ Variable ${variable.name} is never modified`);
276
}
277
278
if (reads.length === 0) {
279
console.log(` ⚠️ Variable ${variable.name} is never read`);
280
}
281
});
282
```
283
284
### Implicit Library Variables
285
286
TypeScript library definitions create implicit variables:
287
288
```typescript
289
// Analyze implicit library variables
290
const globalScope = scopeManager.globalScope;
291
if (globalScope) {
292
const implicitVars = globalScope.variables.filter(v =>
293
v instanceof ImplicitLibVariable
294
);
295
296
console.log('Implicit library variables:');
297
implicitVars.forEach(variable => {
298
const implicitVar = variable as ImplicitLibVariable;
299
console.log(` ${implicitVar.name}`);
300
console.log(` Type variable: ${implicitVar.isTypeVariable}`);
301
console.log(` Value variable: ${implicitVar.isValueVariable}`);
302
console.log(` Writable: ${implicitVar.writeable}`);
303
});
304
}
305
306
// Common implicit variables from TypeScript libs:
307
// - console (from lib.dom.d.ts or lib.node.d.ts)
308
// - window, document (from lib.dom.d.ts)
309
// - process, Buffer (from @types/node)
310
// - Promise, Array, Object (from lib.es*.d.ts)
311
```
312
313
### Variable Scope Relationships
314
315
Understanding how variables relate to their scopes:
316
317
```typescript
318
// Analyze variable-scope relationships
319
scopeManager.scopes.forEach(scope => {
320
console.log(`\nScope: ${scope.type}`);
321
console.log(`Variables defined in this scope: ${scope.set.size}`);
322
323
// Variables defined directly in this scope
324
scope.set.forEach((variable, name) => {
325
console.log(` Owns: ${name}`);
326
});
327
328
// All variables accessible in this scope
329
console.log(`Total accessible variables: ${scope.variables.length}`);
330
331
// Check variable scope chain
332
scope.variables.forEach(variable => {
333
if (variable.scope === scope) {
334
console.log(` Local: ${variable.name}`);
335
} else {
336
console.log(` Inherited: ${variable.name} (from ${variable.scope.type})`);
337
}
338
});
339
});
340
```