0
# Symbol and Declaration Analysis
1
2
Tools for working with TypeScript symbols, declarations, and their relationships to source files and libraries. These utilities help understand the origin and context of types and values in TypeScript code.
3
4
## Capabilities
5
6
### Declaration Resolution
7
8
Functions for finding and analyzing TypeScript declarations.
9
10
```typescript { .api }
11
/**
12
* Gets the declaration for the given variable
13
*/
14
function getDeclaration(
15
services: ParserServicesWithTypeInformation,
16
node: TSESTree.Node
17
): ts.Declaration | null;
18
```
19
20
**Usage Examples:**
21
22
```typescript
23
import { getDeclaration } from "@typescript-eslint/type-utils";
24
25
// In an ESLint rule analyzing variable declarations
26
export default {
27
create(context) {
28
const services = context.parserServices;
29
30
return {
31
Identifier(node) {
32
const declaration = getDeclaration(services, node);
33
34
if (declaration) {
35
// Analyze the declaration
36
switch (declaration.kind) {
37
case ts.SyntaxKind.VariableDeclaration:
38
console.log("Variable declaration found");
39
break;
40
case ts.SyntaxKind.FunctionDeclaration:
41
console.log("Function declaration found");
42
break;
43
case ts.SyntaxKind.ClassDeclaration:
44
console.log("Class declaration found");
45
break;
46
case ts.SyntaxKind.InterfaceDeclaration:
47
console.log("Interface declaration found");
48
break;
49
case ts.SyntaxKind.TypeAliasDeclaration:
50
console.log("Type alias declaration found");
51
break;
52
}
53
54
// Check declaration location
55
const sourceFile = declaration.getSourceFile();
56
console.log(`Declared in: ${sourceFile.fileName}`);
57
}
58
}
59
};
60
}
61
};
62
```
63
64
### Symbol Origin Analysis
65
66
Functions for determining where symbols and types come from.
67
68
```typescript { .api }
69
/**
70
* Checks if a symbol comes from TypeScript's default library
71
*/
72
function isSymbolFromDefaultLibrary(
73
program: ts.Program,
74
symbol: ts.Symbol | undefined
75
): boolean;
76
```
77
78
**Usage Examples:**
79
80
```typescript
81
import { isSymbolFromDefaultLibrary } from "@typescript-eslint/type-utils";
82
83
// In an ESLint rule checking for built-in vs user-defined types
84
export default {
85
create(context) {
86
const services = context.parserServices;
87
const program = services.program;
88
const checker = program.getTypeChecker();
89
90
return {
91
TSTypeReference(node) {
92
const tsNode = services.esTreeNodeToTSNodeMap.get(node);
93
const type = checker.getTypeAtLocation(tsNode);
94
const symbol = type.symbol || type.aliasSymbol;
95
96
if (isSymbolFromDefaultLibrary(program, symbol)) {
97
// It's a built-in TypeScript type
98
context.report({
99
node,
100
messageId: "builtinTypeUsage",
101
data: { typeName: symbol?.name }
102
});
103
} else {
104
// It's a user-defined type
105
context.report({
106
node,
107
messageId: "customTypeUsage",
108
data: { typeName: symbol?.name }
109
});
110
}
111
}
112
};
113
}
114
};
115
```
116
117
### Source File Analysis
118
119
Functions for analyzing the source files where symbols are defined.
120
121
```typescript { .api }
122
/**
123
* Gets the source file for a given node
124
* @deprecated Use node.getSourceFile() directly instead
125
*/
126
function getSourceFileOfNode(node: ts.Node): ts.SourceFile;
127
```
128
129
**Usage Examples:**
130
131
```typescript
132
import { getSourceFileOfNode } from "@typescript-eslint/type-utils";
133
134
// Note: This function is deprecated, prefer node.getSourceFile()
135
function analyzeNodeLocation(tsNode: ts.Node): {
136
fileName: string;
137
isDeclarationFile: boolean;
138
isExternal: boolean;
139
} {
140
// Preferred approach
141
const sourceFile = tsNode.getSourceFile();
142
143
// Deprecated approach (for reference)
144
// const sourceFile = getSourceFileOfNode(tsNode);
145
146
return {
147
fileName: sourceFile.fileName,
148
isDeclarationFile: sourceFile.isDeclarationFile,
149
isExternal: sourceFile.fileName.includes('node_modules')
150
};
151
}
152
```
153
154
## Advanced Symbol Analysis Patterns
155
156
### Declaration Context Analysis
157
158
```typescript
159
// Example: Comprehensive declaration analysis
160
import { getDeclaration, isSymbolFromDefaultLibrary } from "@typescript-eslint/type-utils";
161
162
function analyzeDeclarationContext(
163
services: ParserServicesWithTypeInformation,
164
node: TSESTree.Identifier
165
): {
166
hasDeclaration: boolean;
167
declarationKind: string | null;
168
isBuiltin: boolean;
169
sourceFile: string | null;
170
isExternal: boolean;
171
} {
172
const program = services.program;
173
const checker = program.getTypeChecker();
174
175
const declaration = getDeclaration(services, node);
176
177
if (!declaration) {
178
return {
179
hasDeclaration: false,
180
declarationKind: null,
181
isBuiltin: false,
182
sourceFile: null,
183
isExternal: false
184
};
185
}
186
187
// Get the symbol for builtin check
188
const tsNode = services.esTreeNodeToTSNodeMap.get(node);
189
const type = checker.getTypeAtLocation(tsNode);
190
const symbol = type.symbol || type.aliasSymbol;
191
192
const sourceFile = declaration.getSourceFile();
193
194
return {
195
hasDeclaration: true,
196
declarationKind: ts.SyntaxKind[declaration.kind],
197
isBuiltin: isSymbolFromDefaultLibrary(program, symbol),
198
sourceFile: sourceFile.fileName,
199
isExternal: sourceFile.fileName.includes('node_modules')
200
};
201
}
202
```
203
204
### Symbol Dependency Tracking
205
206
```typescript
207
// Example: Tracking symbol dependencies across files
208
import { getDeclaration, isSymbolFromDefaultLibrary } from "@typescript-eslint/type-utils";
209
210
interface SymbolDependency {
211
name: string;
212
sourceFile: string;
213
isBuiltin: boolean;
214
isExternal: boolean;
215
declarationKind: string;
216
}
217
218
function trackSymbolDependencies(
219
services: ParserServicesWithTypeInformation,
220
rootNode: TSESTree.Node
221
): SymbolDependency[] {
222
const dependencies: SymbolDependency[] = [];
223
const visited = new Set<string>();
224
const program = services.program;
225
const checker = program.getTypeChecker();
226
227
function visitNode(node: TSESTree.Node) {
228
if (node.type === "Identifier") {
229
const declaration = getDeclaration(services, node);
230
231
if (declaration) {
232
const sourceFile = declaration.getSourceFile();
233
const key = `${node.name}:${sourceFile.fileName}`;
234
235
if (!visited.has(key)) {
236
visited.add(key);
237
238
const tsNode = services.esTreeNodeToTSNodeMap.get(node);
239
const type = checker.getTypeAtLocation(tsNode);
240
const symbol = type.symbol || type.aliasSymbol;
241
242
dependencies.push({
243
name: node.name,
244
sourceFile: sourceFile.fileName,
245
isBuiltin: isSymbolFromDefaultLibrary(program, symbol),
246
isExternal: sourceFile.fileName.includes('node_modules'),
247
declarationKind: ts.SyntaxKind[declaration.kind]
248
});
249
}
250
}
251
}
252
253
// Recursively visit child nodes
254
// (Implementation would traverse AST)
255
}
256
257
visitNode(rootNode);
258
return dependencies;
259
}
260
```
261
262
### Library vs User Code Classification
263
264
```typescript
265
// Example: Classifying symbols by their origin
266
import { isSymbolFromDefaultLibrary, getDeclaration } from "@typescript-eslint/type-utils";
267
268
enum SymbolOrigin {
269
Builtin = "builtin",
270
External = "external",
271
UserCode = "userCode",
272
Unknown = "unknown"
273
}
274
275
function classifySymbolOrigin(
276
services: ParserServicesWithTypeInformation,
277
node: TSESTree.Identifier
278
): SymbolOrigin {
279
const program = services.program;
280
const checker = program.getTypeChecker();
281
282
// Check if it's a builtin TypeScript type
283
const tsNode = services.esTreeNodeToTSNodeMap.get(node);
284
const type = checker.getTypeAtLocation(tsNode);
285
const symbol = type.symbol || type.aliasSymbol;
286
287
if (isSymbolFromDefaultLibrary(program, symbol)) {
288
return SymbolOrigin.Builtin;
289
}
290
291
// Check declaration location
292
const declaration = getDeclaration(services, node);
293
294
if (!declaration) {
295
return SymbolOrigin.Unknown;
296
}
297
298
const sourceFile = declaration.getSourceFile();
299
const fileName = sourceFile.fileName;
300
301
if (fileName.includes('node_modules')) {
302
return SymbolOrigin.External;
303
}
304
305
return SymbolOrigin.UserCode;
306
}
307
308
// Usage in ESLint rule
309
export default {
310
create(context) {
311
const services = context.parserServices;
312
313
return {
314
Identifier(node) {
315
const origin = classifySymbolOrigin(services, node);
316
317
switch (origin) {
318
case SymbolOrigin.Builtin:
319
// Handle builtin types
320
break;
321
case SymbolOrigin.External:
322
// Handle external library symbols
323
break;
324
case SymbolOrigin.UserCode:
325
// Handle user-defined symbols
326
break;
327
case SymbolOrigin.Unknown:
328
// Handle unknown symbols
329
break;
330
}
331
}
332
};
333
}
334
};
335
```
336
337
### Declaration Relationship Analysis
338
339
```typescript
340
// Example: Analyzing relationships between declarations
341
import { getDeclaration } from "@typescript-eslint/type-utils";
342
343
interface DeclarationRelationship {
344
node: TSESTree.Identifier;
345
declaration: ts.Declaration;
346
parent: ts.Declaration | null;
347
children: ts.Declaration[];
348
siblings: ts.Declaration[];
349
}
350
351
function analyzeDeclarationRelationships(
352
services: ParserServicesWithTypeInformation,
353
identifiers: TSESTree.Identifier[]
354
): DeclarationRelationship[] {
355
const relationships: DeclarationRelationship[] = [];
356
357
identifiers.forEach(node => {
358
const declaration = getDeclaration(services, node);
359
360
if (declaration) {
361
const parent = declaration.parent;
362
const children: ts.Declaration[] = [];
363
const siblings: ts.Declaration[] = [];
364
365
// Find children (for classes, interfaces, etc.)
366
if (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) {
367
declaration.members?.forEach(member => {
368
if (ts.isMethodDeclaration(member) || ts.isPropertyDeclaration(member)) {
369
children.push(member);
370
}
371
});
372
}
373
374
// Find siblings (other declarations in same scope)
375
if (parent && 'statements' in parent) {
376
(parent as any).statements?.forEach((stmt: ts.Statement) => {
377
if (stmt !== declaration && ts.isDeclaration(stmt)) {
378
siblings.push(stmt);
379
}
380
});
381
}
382
383
relationships.push({
384
node,
385
declaration,
386
parent: parent as ts.Declaration || null,
387
children,
388
siblings
389
});
390
}
391
});
392
393
return relationships;
394
}
395
```