or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ast-utils.mdeslint-utils.mdindex.mdjson-schema.mdscope-analysis.mdts-eslint.mdts-estree.md

scope-analysis.mddocs/

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

```