0
# Scope Analysis
1
2
Utilities for analyzing variable scopes, references, and bindings in TypeScript code. This module provides comprehensive scope analysis capabilities essential for understanding variable usage, detecting unused variables, and tracking references across different scopes.
3
4
## Capabilities
5
6
### Scope Analysis Function
7
8
Core function for analyzing AST and creating scope managers.
9
10
```typescript { .api }
11
/**
12
* Analyzes an AST to create a scope manager with variable and reference information
13
* @param ast - Root AST node to analyze
14
* @param options - Optional analysis configuration
15
* @returns ScopeManager instance containing all scope information
16
*/
17
function analyze(ast: TSESTree.Node, options?: AnalysisOptions): ScopeManager;
18
19
/**
20
* Configuration options for scope analysis
21
*/
22
interface AnalysisOptions {
23
/** Whether to be optimistic about dynamic references */
24
optimistic?: boolean;
25
/** Whether to support directive prologues */
26
directive?: boolean;
27
/** Whether to ignore eval() calls in scope analysis */
28
ignoreEval?: boolean;
29
/** Whether to create Node.js-style global scope */
30
nodejsScope?: boolean;
31
/** Whether code is in implied strict mode */
32
impliedStrict?: boolean;
33
/** Fallback function for unrecognized AST nodes */
34
fallback?: string | ((node: any) => string[]);
35
/** Source type: 'module' or 'script' */
36
sourceType?: 'module' | 'script';
37
/** ECMAScript version */
38
ecmaVersion?: number;
39
/** ECMAScript features configuration */
40
ecmaFeatures?: {
41
globalReturn?: boolean;
42
impliedStrict?: boolean;
43
jsx?: boolean;
44
};
45
}
46
```
47
48
### Scope Manager
49
50
Central manager for all scopes in an analyzed AST.
51
52
```typescript { .api }
53
/**
54
* Manages all scopes in an analyzed AST
55
*/
56
interface ScopeManager {
57
/** All scopes in the AST */
58
readonly scopes: Scope[];
59
/** Global scope (null if not available) */
60
readonly globalScope: GlobalScope | null;
61
/** Current scope being processed */
62
readonly __currentScope: Scope | null;
63
/** Analysis options used */
64
readonly __options: Required<AnalysisOptions>;
65
/** Node to scope mapping */
66
readonly __nodeToScope: WeakMap<TSESTree.Node, Scope>;
67
/** Declared variables cache */
68
readonly __declaredVariables: WeakMap<TSESTree.Node, Variable[]>;
69
70
/**
71
* Acquires the scope for a given AST node
72
* @param node - AST node to get scope for
73
* @param inner - Whether to get inner scope
74
* @returns Scope for the node, or null if not found
75
*/
76
acquire(node: TSESTree.Node, inner?: boolean): Scope | null;
77
78
/**
79
* Releases a scope (cleanup)
80
* @param scope - Scope to release
81
*/
82
release(scope: Scope): void;
83
84
/**
85
* Gets all variables declared by a specific AST node
86
* @param node - AST node that declares variables
87
* @returns Array of variables declared by the node
88
*/
89
getDeclaredVariables(node: TSESTree.Node): Variable[];
90
91
/**
92
* Checks if a scope is strict mode
93
* @param scope - Scope to check
94
* @returns True if scope is in strict mode
95
*/
96
isStrictModeSupported(): boolean;
97
98
/**
99
* Checks if global return is supported
100
* @returns True if global return is supported
101
*/
102
isGlobalReturn(): boolean;
103
104
/**
105
* Checks if implied strict mode is enabled
106
* @returns True if implied strict mode is enabled
107
*/
108
isImpliedStrict(): boolean;
109
110
/**
111
* Checks if the source type is module
112
* @returns True if source type is module
113
*/
114
isModule(): boolean;
115
116
/**
117
* Gets the scope that contains the given node
118
* @param node - AST node to find scope for
119
* @returns Scope containing the node
120
*/
121
__get(node: TSESTree.Node): Scope | null;
122
}
123
```
124
125
### Scope Types
126
127
Different types of scopes with their specific characteristics.
128
129
```typescript { .api }
130
/**
131
* Union type of all possible scope types
132
*/
133
type ScopeType =
134
| 'block'
135
| 'catch'
136
| 'class'
137
| 'for'
138
| 'function'
139
| 'function-expression-name'
140
| 'global'
141
| 'module'
142
| 'switch'
143
| 'with'
144
| 'conditional-type'
145
| 'function-type'
146
| 'mapped-type'
147
| 'ts-enum'
148
| 'ts-module'
149
| 'type';
150
151
/**
152
* Base scope interface with common properties and methods
153
*/
154
interface Scope {
155
/** Type of this scope */
156
readonly type: ScopeType;
157
/** Whether this scope is in strict mode */
158
readonly isStrict: boolean;
159
/** Parent scope (null for global scope) */
160
readonly upper: Scope | null;
161
/** Direct child scopes */
162
readonly childScopes: Scope[];
163
/** Nearest function or global scope */
164
readonly variableScope: Scope;
165
/** AST node that created this scope */
166
readonly block: TSESTree.Node;
167
/** Variables defined in this scope */
168
readonly variables: Variable[];
169
/** References to variables in this scope */
170
readonly references: Reference[];
171
/** Map of variable names to Variable objects */
172
readonly set: Map<string, Variable>;
173
/** References that go through this scope to outer scopes */
174
readonly through: Reference[];
175
/** Function parameter names (function scopes only) */
176
readonly functionExpressionScope?: boolean;
177
178
/**
179
* Checks if this scope contains a variable with the given name
180
* @param name - Variable name to check
181
* @returns True if variable exists in this scope
182
*/
183
__shouldStaticallyClose(scopeManager: ScopeManager): boolean;
184
185
/**
186
* Closes this scope
187
* @param scopeManager - Scope manager instance
188
*/
189
__close(scopeManager: ScopeManager): void;
190
191
/**
192
* Checks if a variable name is defined in this scope
193
* @param name - Variable name to check
194
* @returns True if variable is defined
195
*/
196
__isValidResolution(ref: Reference, variable: Variable): boolean;
197
198
/**
199
* Adds a variable to this scope
200
* @param variable - Variable to add
201
*/
202
__addVariable(variable: Variable): void;
203
204
/**
205
* Removes a variable from this scope
206
* @param variable - Variable to remove
207
*/
208
__removeVariable(variable: Variable): void;
209
210
/**
211
* Defines a variable in this scope
212
* @param name - Variable name
213
* @param set - Variable set to add to
214
* @param variables - Variables array to add to
215
* @param node - AST node that defines the variable
216
* @param def - Variable definition
217
*/
218
__define(name: string, set: Map<string, Variable>, variables: Variable[], node: TSESTree.Node, def: Definition): void;
219
220
/**
221
* References a variable from this scope
222
* @param ref - Reference to add
223
*/
224
__referencing(ref: Reference): void;
225
226
/**
227
* Detaches a reference from this scope
228
* @param ref - Reference to detach
229
*/
230
__detach(): void;
231
232
/**
233
* Checks if this scope contains the given node
234
* @param node - AST node to check
235
* @returns True if scope contains the node
236
*/
237
__isValidResolution(reference: Reference, variable: Variable): boolean;
238
}
239
```
240
241
### Specific Scope Types
242
243
Specialized scope types for different contexts.
244
245
```typescript { .api }
246
/**
247
* Global scope - the root scope containing global variables
248
*/
249
interface GlobalScope extends Scope {
250
type: 'global';
251
upper: null;
252
variableScope: GlobalScope;
253
}
254
255
/**
256
* Module scope - for ES modules
257
*/
258
interface ModuleScope extends Scope {
259
type: 'module';
260
upper: GlobalScope;
261
variableScope: ModuleScope;
262
}
263
264
/**
265
* Function scope - created by function declarations and expressions
266
*/
267
interface FunctionScope extends Scope {
268
type: 'function';
269
variableScope: FunctionScope;
270
/** Whether this is a function expression name scope */
271
functionExpressionScope: boolean;
272
}
273
274
/**
275
* Function expression name scope - for named function expressions
276
*/
277
interface FunctionExpressionNameScope extends Scope {
278
type: 'function-expression-name';
279
functionExpressionScope: true;
280
}
281
282
/**
283
* Block scope - created by block statements, for/while loops, etc.
284
*/
285
interface BlockScope extends Scope {
286
type: 'block';
287
}
288
289
/**
290
* Class scope - created by class declarations and expressions
291
*/
292
interface ClassScope extends Scope {
293
type: 'class';
294
}
295
296
/**
297
* Catch scope - created by catch clauses
298
*/
299
interface CatchScope extends Scope {
300
type: 'catch';
301
}
302
303
/**
304
* With scope - created by with statements
305
*/
306
interface WithScope extends Scope {
307
type: 'with';
308
}
309
310
/**
311
* Switch scope - created by switch statements
312
*/
313
interface SwitchScope extends Scope {
314
type: 'switch';
315
}
316
317
/**
318
* For scope - created by for/for-in/for-of statements
319
*/
320
interface ForScope extends Scope {
321
type: 'for';
322
}
323
324
/**
325
* TypeScript enum scope
326
*/
327
interface TSEnumScope extends Scope {
328
type: 'ts-enum';
329
}
330
331
/**
332
* TypeScript module scope
333
*/
334
interface TSModuleScope extends Scope {
335
type: 'ts-module';
336
}
337
338
/**
339
* TypeScript type scope
340
*/
341
interface TypeScope extends Scope {
342
type: 'type';
343
}
344
345
/**
346
* TypeScript conditional type scope
347
*/
348
interface ConditionalTypeScope extends Scope {
349
type: 'conditional-type';
350
}
351
352
/**
353
* TypeScript function type scope
354
*/
355
interface FunctionTypeScope extends Scope {
356
type: 'function-type';
357
}
358
359
/**
360
* TypeScript mapped type scope
361
*/
362
interface MappedTypeScope extends Scope {
363
type: 'mapped-type';
364
}
365
```
366
367
### Variables
368
369
Variable representation with definition and usage information.
370
371
```typescript { .api }
372
/**
373
* Represents a variable with its definitions and references
374
*/
375
interface Variable {
376
/** Variable name */
377
readonly name: string;
378
/** Identifiers where this variable is defined */
379
readonly identifiers: TSESTree.Identifier[];
380
/** References to this variable */
381
readonly references: Reference[];
382
/** Variable definitions */
383
readonly defs: Definition[];
384
/** Scope where this variable is defined */
385
readonly scope: Scope;
386
/** Stack of definitions (for complex cases) */
387
readonly stack?: boolean;
388
/** Whether this is a type-only variable */
389
readonly tainted?: boolean;
390
/** ESLint-specific: whether variable is used */
391
eslintUsed?: boolean;
392
/** ESLint-specific: whether to explicitly mark as used */
393
eslintExplicitGlobal?: boolean;
394
/** ESLint-specific: whether variable is defined implicitly */
395
eslintImplicitGlobalSetting?: 'readonly' | 'writable' | 'off';
396
/** ESLint-specific: line where variable is defined */
397
eslintExplicitGlobalComments?: TSESTree.Comment[];
398
}
399
```
400
401
### References
402
403
Variable reference information tracking usage locations.
404
405
```typescript { .api }
406
/**
407
* Represents a reference to a variable
408
*/
409
interface Reference {
410
/** Identifier node of the reference */
411
readonly identifier: TSESTree.Identifier;
412
/** Scope containing this reference */
413
readonly from: Scope;
414
/** Variable being referenced (null if unresolved) */
415
resolved: Variable | null;
416
/** Type of reference */
417
readonly flag: Reference.Flag;
418
/** Whether this is a write reference */
419
readonly isWrite: boolean;
420
/** Whether this is a read reference */
421
readonly isRead: boolean;
422
/** Whether this is a read-write reference */
423
readonly isReadWrite: boolean;
424
/** Whether this reference creates a new binding */
425
readonly isReadOnly: boolean;
426
/** Partial flag information */
427
readonly partial: boolean;
428
/** Whether this reference initializes the variable */
429
init?: boolean;
430
/** Write expression (for write references) */
431
writeExpr?: TSESTree.Node;
432
/** Whether reference may throw */
433
maybeImplicitGlobal?: boolean;
434
}
435
436
declare namespace Reference {
437
/**
438
* Reference type flags
439
*/
440
const enum Flag {
441
Read = 0x1,
442
Write = 0x2,
443
ReadWrite = Read | Write,
444
}
445
}
446
```
447
448
### Variable Definitions
449
450
Information about where and how variables are defined.
451
452
```typescript { .api }
453
/**
454
* Base interface for variable definitions
455
*/
456
interface Definition {
457
/** Type of definition */
458
readonly type: Definition.Type;
459
/** Variable name being defined */
460
readonly name: TSESTree.Identifier;
461
/** AST node that defines the variable */
462
readonly node: TSESTree.Node;
463
/** Parent node of the definition */
464
readonly parent?: TSESTree.Node;
465
/** Index in parent node (for array-like parents) */
466
readonly index?: number;
467
/** Kind of definition (var, let, const, etc.) */
468
readonly kind?: string;
469
}
470
471
declare namespace Definition {
472
/**
473
* Types of variable definitions
474
*/
475
type Type =
476
| 'CatchClause'
477
| 'ClassName'
478
| 'FunctionName'
479
| 'ImplicitGlobalVariable'
480
| 'ImportBinding'
481
| 'Parameter'
482
| 'TSEnumName'
483
| 'TSEnumMember'
484
| 'TSModuleName'
485
| 'Type'
486
| 'Variable';
487
}
488
489
/**
490
* Variable declaration definition (var, let, const)
491
*/
492
interface VariableDefinition extends Definition {
493
type: 'Variable';
494
node: TSESTree.VariableDeclarator;
495
parent: TSESTree.VariableDeclaration;
496
kind: 'var' | 'let' | 'const';
497
}
498
499
/**
500
* Function parameter definition
501
*/
502
interface ParameterDefinition extends Definition {
503
type: 'Parameter';
504
node: TSESTree.Parameter;
505
rest?: boolean;
506
}
507
508
/**
509
* Function name definition
510
*/
511
interface FunctionNameDefinition extends Definition {
512
type: 'FunctionName';
513
node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression;
514
}
515
516
/**
517
* Class name definition
518
*/
519
interface ClassNameDefinition extends Definition {
520
type: 'ClassName';
521
node: TSESTree.ClassDeclaration | TSESTree.ClassExpression;
522
}
523
524
/**
525
* Import binding definition
526
*/
527
interface ImportBindingDefinition extends Definition {
528
type: 'ImportBinding';
529
node: TSESTree.ImportSpecifier | TSESTree.ImportDefaultSpecifier | TSESTree.ImportNamespaceSpecifier;
530
parent: TSESTree.ImportDeclaration;
531
}
532
533
/**
534
* Catch clause parameter definition
535
*/
536
interface CatchClauseDefinition extends Definition {
537
type: 'CatchClause';
538
node: TSESTree.CatchClause;
539
}
540
541
/**
542
* TypeScript enum name definition
543
*/
544
interface TSEnumNameDefinition extends Definition {
545
type: 'TSEnumName';
546
node: TSESTree.TSEnumDeclaration;
547
}
548
549
/**
550
* TypeScript enum member definition
551
*/
552
interface TSEnumMemberDefinition extends Definition {
553
type: 'TSEnumMember';
554
node: TSESTree.TSEnumMember;
555
}
556
557
/**
558
* TypeScript module name definition
559
*/
560
interface TSModuleNameDefinition extends Definition {
561
type: 'TSModuleName';
562
node: TSESTree.TSModuleDeclaration;
563
}
564
565
/**
566
* Type definition (interface, type alias)
567
*/
568
interface TypeDefinition extends Definition {
569
type: 'Type';
570
node: TSESTree.TSInterfaceDeclaration | TSESTree.TSTypeAliasDeclaration;
571
}
572
573
/**
574
* Implicit global variable definition
575
*/
576
interface ImplicitGlobalVariableDefinition extends Definition {
577
type: 'ImplicitGlobalVariable';
578
node: TSESTree.Program;
579
}
580
```
581
582
### Version Information
583
584
```typescript { .api }
585
/**
586
* ESLint scope version string
587
*/
588
const version: string;
589
```
590
591
## Usage Examples
592
593
```typescript
594
import { TSESLintScope, TSESTree } from "@typescript-eslint/experimental-utils";
595
596
// Analyze an AST to create scopes
597
function analyzeScopes(ast: TSESTree.Program): TSESLintScope.ScopeManager {
598
const scopeManager = TSESLintScope.analyze(ast, {
599
ecmaVersion: 2020,
600
sourceType: 'module',
601
ecmaFeatures: {
602
jsx: true
603
}
604
});
605
606
return scopeManager;
607
}
608
609
// Find all variables in global scope
610
function findGlobalVariables(scopeManager: TSESLintScope.ScopeManager): TSESLintScope.Variable[] {
611
const globalScope = scopeManager.globalScope;
612
return globalScope ? globalScope.variables : [];
613
}
614
615
// Check if a variable is used
616
function isVariableUsed(variable: TSESLintScope.Variable): boolean {
617
return variable.references.some(ref => ref.isRead);
618
}
619
620
// Find unused variables in a scope
621
function findUnusedVariables(scope: TSESLintScope.Scope): TSESLintScope.Variable[] {
622
return scope.variables.filter(variable => {
623
// Skip function names and parameters that might be required
624
const isParameter = variable.defs.some(def => def.type === 'Parameter');
625
const isFunctionName = variable.defs.some(def => def.type === 'FunctionName');
626
627
if (isParameter || isFunctionName) {
628
return false;
629
}
630
631
// Check if variable has any read references
632
return !variable.references.some(ref => ref.isRead);
633
});
634
}
635
636
// Track variable references across scopes
637
function trackVariableUsage(scopeManager: TSESLintScope.ScopeManager, variableName: string): void {
638
scopeManager.scopes.forEach(scope => {
639
const variable = scope.set.get(variableName);
640
if (variable) {
641
console.log(`Variable '${variableName}' defined in ${scope.type} scope`);
642
643
variable.references.forEach(ref => {
644
const refType = ref.isRead ? 'read' : ref.isWrite ? 'write' : 'unknown';
645
console.log(` Referenced as ${refType} at line ${ref.identifier.loc.start.line}`);
646
});
647
}
648
});
649
}
650
651
// Find variables that escape their scope
652
function findEscapingVariables(scope: TSESLintScope.Scope): TSESLintScope.Variable[] {
653
return scope.variables.filter(variable => {
654
return variable.references.some(ref => ref.from !== scope);
655
});
656
}
657
658
// Analyze function parameters
659
function analyzeFunctionParameters(functionScope: TSESLintScope.FunctionScope): void {
660
const parameters = functionScope.variables.filter(variable =>
661
variable.defs.some(def => def.type === 'Parameter')
662
);
663
664
parameters.forEach(param => {
665
const isUsed = param.references.some(ref => ref.isRead);
666
const paramDef = param.defs.find(def => def.type === 'Parameter') as TSESLintScope.ParameterDefinition;
667
668
console.log(`Parameter '${param.name}' is ${isUsed ? 'used' : 'unused'}`);
669
if (paramDef.rest) {
670
console.log(` Rest parameter`);
671
}
672
});
673
}
674
675
// Check scope hierarchy
676
function printScopeHierarchy(scope: TSESLintScope.Scope, indent = 0): void {
677
const indentStr = ' '.repeat(indent);
678
console.log(`${indentStr}${scope.type} scope (${scope.variables.length} variables)`);
679
680
scope.childScopes.forEach(childScope => {
681
printScopeHierarchy(childScope, indent + 1);
682
});
683
}
684
685
// Find variable definition location
686
function findVariableDefinition(variable: TSESLintScope.Variable): TSESTree.Node | null {
687
const definition = variable.defs[0];
688
if (!definition) return null;
689
690
switch (definition.type) {
691
case 'Variable':
692
return definition.node; // VariableDeclarator
693
case 'FunctionName':
694
return definition.node; // FunctionDeclaration
695
case 'Parameter':
696
return definition.node; // Parameter node
697
case 'ImportBinding':
698
return definition.node; // ImportSpecifier
699
default:
700
return definition.node;
701
}
702
}
703
704
// Usage in an ESLint rule
705
const rule: TSESLint.RuleModule<'unusedVariable', []> = {
706
meta: {
707
type: 'suggestion',
708
messages: {
709
unusedVariable: 'Variable "{{name}}" is defined but never used'
710
},
711
schema: []
712
},
713
create(context) {
714
return {
715
'Program:exit'(node: TSESTree.Program) {
716
const sourceCode = context.getSourceCode();
717
const scopeManager = sourceCode.scopeManager;
718
719
scopeManager.scopes.forEach(scope => {
720
const unusedVars = findUnusedVariables(scope);
721
722
unusedVars.forEach(variable => {
723
const def = variable.identifiers[0];
724
if (def) {
725
context.report({
726
node: def,
727
messageId: 'unusedVariable',
728
data: { name: variable.name }
729
});
730
}
731
});
732
});
733
}
734
};
735
}
736
};
737
```