0
# AST Utilities
1
2
The `ASTUtils` namespace provides comprehensive utilities for Abstract Syntax Tree manipulation and analysis in TypeScript ESLint rules.
3
4
## Import
5
6
```typescript { .api }
7
import { ASTUtils } from '@typescript-eslint/utils';
8
```
9
10
## Node Type Predicates
11
12
### Basic Type Checking
13
14
```typescript { .api }
15
// Generic node type checker
16
function isNodeOfType<NodeType extends AST_NODE_TYPES>(
17
nodeType: NodeType
18
): (node: TSESTree.Node | null | undefined) => node is Extract<TSESTree.Node, { type: NodeType }>;
19
20
// Multiple node types checker
21
function isNodeOfTypes<NodeTypes extends readonly AST_NODE_TYPES[]>(
22
nodeTypes: NodeTypes
23
): (node: TSESTree.Node | null | undefined) => node is Extract<TSESTree.Node, { type: NodeTypes[number] }>;
24
25
// Usage examples
26
const isFunctionDeclaration = ASTUtils.isNodeOfType(AST_NODE_TYPES.FunctionDeclaration);
27
const isVariableOrFunction = ASTUtils.isNodeOfTypes([
28
AST_NODE_TYPES.VariableDeclaration,
29
AST_NODE_TYPES.FunctionDeclaration
30
]);
31
32
if (isFunctionDeclaration(node)) {
33
// node is typed as TSESTree.FunctionDeclaration
34
console.log(node.id?.name);
35
}
36
```
37
38
### Conditional Type Checking
39
40
```typescript { .api }
41
// Node type with additional conditions
42
function isNodeOfTypeWithConditions<
43
NodeType extends AST_NODE_TYPES,
44
ExtractedNode extends Extract<TSESTree.Node, { type: NodeType }>,
45
Conditions extends Partial<ExtractedNode>
46
>(
47
nodeType: NodeType,
48
conditions: Conditions
49
): (node: TSESTree.Node | null | undefined) => node is ExtractedNode & Conditions;
50
51
// Usage example
52
const isAsyncFunction = ASTUtils.isNodeOfTypeWithConditions(
53
AST_NODE_TYPES.FunctionDeclaration,
54
{ async: true }
55
);
56
57
if (isAsyncFunction(node)) {
58
// node is TSESTree.FunctionDeclaration with async: true
59
}
60
```
61
62
### Function Predicates
63
64
```typescript { .api }
65
// Check for any function node type
66
function isFunction(node: TSESTree.Node | null | undefined): node is TSESTree.Function;
67
68
// Check for TypeScript function type
69
function isFunctionType(node: TSESTree.Node | null | undefined): node is TSESTree.TSFunctionType;
70
71
// Check for function or function type
72
function isFunctionOrFunctionType(
73
node: TSESTree.Node | null | undefined
74
): node is TSESTree.Function | TSESTree.TSFunctionType;
75
76
// Specific TypeScript function types
77
function isTSFunctionType(node: TSESTree.Node | null | undefined): node is TSESTree.TSFunctionType;
78
function isTSConstructorType(node: TSESTree.Node | null | undefined): node is TSESTree.TSConstructorType;
79
80
// Usage examples
81
if (ASTUtils.isFunction(node)) {
82
// Handle arrow functions, function declarations, function expressions
83
const params = node.params;
84
const body = node.body;
85
}
86
87
if (ASTUtils.isFunctionType(node)) {
88
// Handle TypeScript function type annotations
89
const returnType = node.returnType;
90
}
91
```
92
93
### Expression Predicates
94
95
```typescript { .api }
96
// Optional chaining call expressions
97
function isOptionalCallExpression(node: TSESTree.Node | null | undefined): node is TSESTree.CallExpression & { optional: true };
98
99
// Logical OR expressions
100
function isLogicalOrOperator(node: TSESTree.Node | null | undefined): node is TSESTree.LogicalExpression & { operator: '||' };
101
102
// Type assertions
103
function isTypeAssertion(node: TSESTree.Node | null | undefined): node is TSESTree.TSTypeAssertion | TSESTree.TSAsExpression;
104
105
// Await expressions
106
function isAwaitExpression(node: TSESTree.Node | null | undefined): node is TSESTree.AwaitExpression;
107
108
// Usage examples
109
if (ASTUtils.isOptionalCallExpression(node)) {
110
// Handle obj?.method() calls
111
const callee = node.callee; // has optional property
112
}
113
114
if (ASTUtils.isTypeAssertion(node)) {
115
// Handle both <Type>expr and expr as Type
116
const expression = node.expression;
117
}
118
```
119
120
### Statement and Declaration Predicates
121
122
```typescript { .api }
123
// Variable declarators
124
function isVariableDeclarator(node: TSESTree.Node | null | undefined): node is TSESTree.VariableDeclarator;
125
126
// Loop statements
127
function isLoop(node: TSESTree.Node | null | undefined): node is TSESTree.DoWhileStatement | TSESTree.ForStatement | TSESTree.ForInStatement | TSESTree.ForOfStatement | TSESTree.WhileStatement;
128
129
// Identifiers
130
function isIdentifier(node: TSESTree.Node | null | undefined): node is TSESTree.Identifier;
131
132
// Usage examples
133
if (ASTUtils.isLoop(node)) {
134
// Handle all loop types uniformly
135
const body = node.body;
136
}
137
138
if (ASTUtils.isVariableDeclarator(node)) {
139
// Handle variable declarations
140
const id = node.id;
141
const init = node.init;
142
}
143
```
144
145
### Class and Method Predicates
146
147
```typescript { .api }
148
// Class or type element nodes
149
function isClassOrTypeElement(node: TSESTree.Node | null | undefined): node is TSESTree.ClassElement | TSESTree.TypeElement;
150
151
// Constructor methods
152
function isConstructor(node: TSESTree.Node | null | undefined): node is TSESTree.MethodDefinition & { kind: 'constructor' };
153
154
// Setter methods
155
function isSetter(node: TSESTree.Node | undefined): node is TSESTree.MethodDefinition & { kind: 'set' } | TSESTree.PropertyDefinition & { kind: 'set' };
156
157
// Usage examples
158
if (ASTUtils.isConstructor(node)) {
159
// Handle constructor methods specifically
160
const params = node.value.params;
161
}
162
163
if (ASTUtils.isSetter(node)) {
164
// Handle setter methods
165
const key = node.key;
166
}
167
```
168
169
## Token Predicates
170
171
### Punctuator Tokens
172
173
```typescript { .api }
174
// Optional chaining punctuator (?.)
175
function isOptionalChainPunctuator(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '?.' };
176
function isNotOptionalChainPunctuator(token: TSESTree.Token | null | undefined): token is Exclude<TSESTree.Token, TSESTree.PunctuatorToken & { value: '?.' }>;
177
178
// Non-null assertion punctuator (!)
179
function isNonNullAssertionPunctuator(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '!' };
180
function isNotNonNullAssertionPunctuator(token: TSESTree.Token | null | undefined): token is Exclude<TSESTree.Token, TSESTree.PunctuatorToken & { value: '!' }>;
181
182
// Common punctuators
183
function isArrowToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '=>' };
184
function isCommaToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: ',' };
185
function isSemicolonToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: ';' };
186
function isColonToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: ':' };
187
188
// Usage examples
189
const tokens = sourceCode.getTokens(node);
190
const arrowToken = tokens.find(ASTUtils.isArrowToken);
191
if (arrowToken) {
192
// Handle arrow function tokens
193
}
194
```
195
196
### Bracket and Brace Tokens
197
198
```typescript { .api }
199
// Opening tokens
200
function isOpeningBraceToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '{' };
201
function isOpeningBracketToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '[' };
202
function isOpeningParenToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '(' };
203
204
// Closing tokens
205
function isClosingBraceToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '}' };
206
function isClosingBracketToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: ']' };
207
function isClosingParenToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: ')' };
208
209
// Negated versions available for all punctuators
210
function isNotOpeningBraceToken(token: TSESTree.Token | null | undefined): boolean;
211
function isNotClosingParenToken(token: TSESTree.Token | null | undefined): boolean;
212
// ... (similar pattern for all punctuators)
213
214
// Usage examples
215
const openBrace = sourceCode.getTokenAfter(node, ASTUtils.isOpeningBraceToken);
216
const closeBrace = sourceCode.getTokenAfter(node, ASTUtils.isClosingBraceToken);
217
```
218
219
### Keyword Tokens
220
221
```typescript { .api }
222
// Keyword token predicates
223
function isAwaitKeyword(token: TSESTree.Token | null | undefined): token is TSESTree.KeywordToken & { value: 'await' };
224
function isTypeKeyword(token: TSESTree.Token | null | undefined): token is TSESTree.KeywordToken & { value: 'type' };
225
function isImportKeyword(token: TSESTree.Token | null | undefined): token is TSESTree.KeywordToken & { value: 'import' };
226
227
// Usage examples
228
const awaitToken = sourceCode.getFirstToken(node, ASTUtils.isAwaitKeyword);
229
if (awaitToken) {
230
// Handle await keyword positioning
231
}
232
```
233
234
### Comment Tokens
235
236
```typescript { .api }
237
// Comment token predicates
238
function isCommentToken(token: TSESTree.Token | null | undefined): token is TSESTree.Comment;
239
function isNotCommentToken(token: TSESTree.Token | null | undefined): token is Exclude<TSESTree.Token, TSESTree.Comment>;
240
241
// Usage examples
242
const nonCommentTokens = sourceCode.getTokens(node).filter(ASTUtils.isNotCommentToken);
243
```
244
245
## Token Conditions
246
247
### Advanced Token Filtering
248
249
```typescript { .api }
250
// Token type with conditions
251
function isTokenOfTypeWithConditions<
252
TokenType extends AST_TOKEN_TYPES,
253
ExtractedToken extends Extract<TSESTree.Token, { type: TokenType }>,
254
Conditions extends Partial<ExtractedToken>
255
>(
256
tokenType: TokenType,
257
conditions: Conditions
258
): (token: TSESTree.Token | null | undefined) => token is ExtractedToken & Conditions;
259
260
// Negated token conditions
261
function isNotTokenOfTypeWithConditions<
262
TokenType extends AST_TOKEN_TYPES,
263
ExtractedToken extends Extract<TSESTree.Token, { type: TokenType }>,
264
Conditions extends Partial<ExtractedToken>
265
>(
266
tokenType: TokenType,
267
conditions: Conditions
268
): (token: TSESTree.Token | null | undefined) => token is Exclude<TSESTree.Token, ExtractedToken & Conditions>;
269
270
// Usage examples
271
const isSpecificPunctuator = ASTUtils.isTokenOfTypeWithConditions(
272
AST_TOKEN_TYPES.Punctuator,
273
{ value: '=>' as const }
274
);
275
276
const isNotArrowFunction = ASTUtils.isNotTokenOfTypeWithConditions(
277
AST_TOKEN_TYPES.Punctuator,
278
{ value: '=>' as const }
279
);
280
```
281
282
## Utility Functions
283
284
### Line Analysis
285
286
```typescript { .api }
287
// Check if tokens are on the same line
288
function isTokenOnSameLine(
289
left: TSESTree.Node | TSESTree.Token,
290
right: TSESTree.Node | TSESTree.Token
291
): boolean;
292
293
// Line break matcher regex
294
const LINEBREAK_MATCHER: RegExp; // /\r\n|[\r\n\u2028\u2029]/
295
296
// Usage examples
297
if (ASTUtils.isTokenOnSameLine(node, nextToken)) {
298
// Tokens are on the same line
299
}
300
301
const hasLineBreak = ASTUtils.LINEBREAK_MATCHER.test(sourceCode.getText(node));
302
```
303
304
## ESLint Integration Utilities
305
306
### Function Analysis
307
308
```typescript { .api }
309
// Get function head location for reporting
310
function getFunctionHeadLocation(
311
node: TSESTree.Function,
312
sourceCode: TSESLint.SourceCode
313
): TSESTree.SourceLocation;
314
315
// Get function name with kind description
316
function getFunctionNameWithKind(
317
node: TSESTree.Function,
318
sourceCode?: TSESLint.SourceCode
319
): string;
320
321
// Usage examples
322
const location = ASTUtils.getFunctionHeadLocation(node, context.getSourceCode());
323
context.report({
324
loc: location,
325
messageId: 'error'
326
});
327
328
const functionDescription = ASTUtils.getFunctionNameWithKind(node);
329
// Returns strings like "function 'myFunc'", "arrow function", "method 'myMethod'"
330
```
331
332
### Property Analysis
333
334
```typescript { .api }
335
// Get property name from property-like nodes
336
function getPropertyName(
337
node: TSESTree.Property | TSESTree.MethodDefinition | TSESTree.PropertyDefinition,
338
initialScope?: TSESLint.Scope.Scope
339
): string | null;
340
341
// Usage examples
342
const propName = ASTUtils.getPropertyName(node);
343
if (propName) {
344
// Handle known property names
345
}
346
```
347
348
### Static Value Analysis
349
350
```typescript { .api }
351
// Get static value if determinable
352
function getStaticValue(
353
node: TSESTree.Node,
354
initialScope?: TSESLint.Scope.Scope
355
): { value: unknown } | null;
356
357
// Get string value if constant
358
function getStringIfConstant(
359
node: TSESTree.Node,
360
initialScope?: TSESLint.Scope.Scope
361
): string | null;
362
363
// Usage examples
364
const staticValue = ASTUtils.getStaticValue(node);
365
if (staticValue) {
366
console.log('Static value:', staticValue.value);
367
}
368
369
const stringValue = ASTUtils.getStringIfConstant(node);
370
if (stringValue) {
371
// Handle constant string values
372
}
373
```
374
375
### Side Effect Analysis
376
377
```typescript { .api }
378
// Check if node has side effects
379
function hasSideEffect(
380
node: TSESTree.Node,
381
sourceCode: TSESLint.SourceCode,
382
options?: { considerGetters?: boolean; considerImplicitTypeConversion?: boolean }
383
): boolean;
384
385
// Usage examples
386
if (!ASTUtils.hasSideEffect(node, context.getSourceCode())) {
387
// Safe to remove or reorder this node
388
}
389
```
390
391
### Parentheses Analysis
392
393
```typescript { .api }
394
// Check if node is parenthesized (overloaded)
395
function isParenthesized(node: TSESTree.Node, sourceCode: TSESLint.SourceCode): boolean;
396
function isParenthesized(times: number, node: TSESTree.Node, sourceCode: TSESLint.SourceCode): boolean;
397
398
// Usage examples
399
if (ASTUtils.isParenthesized(node, sourceCode)) {
400
// Node has parentheses around it
401
}
402
403
if (ASTUtils.isParenthesized(2, node, sourceCode)) {
404
// Node has at least 2 levels of parentheses: ((node))
405
}
406
```
407
408
## Pattern Matching
409
410
### PatternMatcher Class
411
412
```typescript { .api }
413
class PatternMatcher {
414
constructor(pattern: RegExp, options?: { escaped?: boolean });
415
416
// Execute pattern matching
417
execAll(str: string): IterableIterator<RegExpExecArray>;
418
419
// Test if pattern matches
420
test(str: string): boolean;
421
422
// Replace matches in string
423
[Symbol.replace](str: string, replacer: string | ((match: string, ...args: any[]) => string)): string;
424
}
425
426
// Usage examples
427
const matcher = new ASTUtils.PatternMatcher(/function\s+(\w+)/g);
428
429
for (const match of matcher.execAll(sourceCode.getText())) {
430
console.log('Found function:', match[1]);
431
}
432
```
433
434
## Reference Tracking
435
436
### ReferenceTracker Class
437
438
```typescript { .api }
439
class ReferenceTracker {
440
constructor(globalScope: TSESLint.Scope.Scope, options?: {
441
mode?: 'strict' | 'legacy';
442
globalObjectNames?: readonly string[];
443
});
444
445
// Iterate over global references
446
iterateGlobalReferences<T>(
447
traceMap: ReferenceTracker.TraceMap<T>
448
): IterableIterator<ReferenceTracker.FoundReference<T>>;
449
450
// Iterate over CommonJS references
451
iterateCjsReferences<T>(
452
traceMap: ReferenceTracker.TraceMap<T>
453
): IterableIterator<ReferenceTracker.FoundReference<T>>;
454
455
// Iterate over ES module references
456
iterateEsModuleReferences<T>(
457
traceMap: ReferenceTracker.TraceMap<T>
458
): IterableIterator<ReferenceTracker.FoundReference<T>>;
459
}
460
461
// Usage examples
462
const tracker = new ASTUtils.ReferenceTracker(context.getScope());
463
464
const traceMap = {
465
React: {
466
createElement: { [ASTUtils.ReferenceTracker.READ]: true }
467
}
468
};
469
470
for (const { node, path } of tracker.iterateGlobalReferences(traceMap)) {
471
// Handle React.createElement references
472
}
473
```
474
475
## Scope Analysis
476
477
### Variable and Scope Utilities
478
479
```typescript { .api }
480
// Find variable by name or node
481
function findVariable(
482
initialScope: TSESLint.Scope.Scope,
483
nameOrNode: string | TSESTree.Identifier
484
): TSESLint.Scope.Variable | null;
485
486
// Get innermost scope containing node
487
function getInnermostScope(
488
initialScope: TSESLint.Scope.Scope,
489
node: TSESTree.Node
490
): TSESLint.Scope.Scope;
491
492
// Usage examples
493
const variable = ASTUtils.findVariable(context.getScope(), 'myVar');
494
if (variable) {
495
// Check variable references and definitions
496
const refs = variable.references;
497
const defs = variable.defs;
498
}
499
500
const innerScope = ASTUtils.getInnermostScope(context.getScope(), node);
501
// Get scope that directly contains the node
502
```
503
504
## Complete Usage Example
505
506
```typescript { .api }
507
import { ESLintUtils, ASTUtils, TSESTree } from '@typescript-eslint/utils';
508
509
const createRule = ESLintUtils.RuleCreator(name => `https://example.com/${name}`);
510
511
export default createRule({
512
name: 'comprehensive-ast-example',
513
meta: {
514
type: 'suggestion',
515
docs: { description: 'Comprehensive AST utilities example' },
516
messages: {
517
optionalCall: 'Consider using optional chaining',
518
parenthesized: 'Unnecessary parentheses detected',
519
sideEffect: 'Expression has side effects'
520
},
521
schema: []
522
},
523
defaultOptions: [],
524
create(context) {
525
const sourceCode = context.getSourceCode();
526
527
return {
528
CallExpression(node) {
529
// Check for optional calls
530
if (ASTUtils.isOptionalCallExpression(node)) {
531
context.report({
532
node,
533
messageId: 'optionalCall'
534
});
535
}
536
537
// Check if parenthesized
538
if (ASTUtils.isParenthesized(node, sourceCode)) {
539
context.report({
540
node,
541
messageId: 'parenthesized'
542
});
543
}
544
545
// Check for side effects
546
if (ASTUtils.hasSideEffect(node, sourceCode)) {
547
context.report({
548
node,
549
messageId: 'sideEffect'
550
});
551
}
552
},
553
554
'Program:exit'() {
555
// Use reference tracker
556
const tracker = new ASTUtils.ReferenceTracker(context.getScope());
557
const traceMap = {
558
console: {
559
log: { [ASTUtils.ReferenceTracker.READ]: true }
560
}
561
};
562
563
for (const { node } of tracker.iterateGlobalReferences(traceMap)) {
564
// Handle console.log references
565
}
566
}
567
};
568
}
569
});
570
```