or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

compiler-options.mdindex.mdnode-analysis.mdsyntax-utilities.mdtype-system.mdusage-analysis.md

node-analysis.mddocs/

0

# Node Analysis

1

2

Node Analysis is the core capability of ts-api-utils, providing comprehensive utilities for working with TypeScript AST (Abstract Syntax Tree) nodes. This module offers type guards, access utilities, and helper functions that are essential for building robust TypeScript tooling, linters, and code analysis tools.

3

4

## Overview

5

6

TypeScript's AST represents source code as a tree of nodes, where each node corresponds to a construct in the language (expressions, statements, declarations, etc.). The Node Analysis module provides three main categories of utilities:

7

8

1. **Type Guards**: Functions that test whether a node is of a specific type

9

2. **Access Utilities**: Functions for determining how expressions are accessed (read, write, delete)

10

3. **Node Utilities**: Helper functions for working with nodes and their properties

11

12

Understanding AST nodes and their relationships is fundamental to TypeScript tooling development, as it allows tools to programmatically analyze and manipulate code structure.

13

14

## Core Concepts

15

16

### AST Nodes

17

18

Every construct in TypeScript code is represented as a node in the AST. Nodes have:

19

- A `kind` property that identifies the node type

20

- Child nodes that represent nested constructs

21

- Properties specific to their node type

22

- Parent relationships for traversing up the tree

23

24

### Type Guards

25

26

Type guards are functions that narrow TypeScript's type system by checking if a node matches a specific pattern. They return `node is SpecificNodeType` to provide type safety:

27

28

```typescript

29

declare const node: ts.Node;

30

31

if (isVariableDeclaration(node)) {

32

// TypeScript now knows node is ts.VariableDeclaration

33

console.log(node.name.getText());

34

}

35

```

36

37

### Node Access Patterns

38

39

Expressions in TypeScript can be accessed in different ways:

40

- **Read**: Getting the value (e.g., `console.log(x)`)

41

- **Write**: Setting the value (e.g., `x = 5`)

42

- **Delete**: Removing the property (e.g., `delete obj.prop`)

43

- **ReadWrite**: Both reading and writing (e.g., `x++`)

44

45

## Node Type Guards

46

47

Type guards enable safe type narrowing for AST nodes. The library provides guards for single nodes, union types, and compound conditions.

48

49

### Single Node Type Guards

50

51

These functions test for specific keyword and token types:

52

53

```typescript

54

function isAbstractKeyword(node: ts.Node): node is ts.AbstractKeyword { .api }

55

function isAccessorKeyword(node: ts.Node): node is ts.AccessorKeyword { .api }

56

function isAnyKeyword(node: ts.Node): node is AnyKeyword { .api }

57

function isAssertKeyword(node: ts.Node): node is ts.AssertKeyword { .api }

58

function isAssertsKeyword(node: ts.Node): node is ts.AssertsKeyword { .api }

59

function isAsyncKeyword(node: ts.Node): node is ts.AsyncKeyword { .api }

60

function isAwaitKeyword(node: ts.Node): node is ts.AwaitKeyword { .api }

61

function isBigIntKeyword(node: ts.Node): node is BigIntKeyword { .api }

62

function isBooleanKeyword(node: ts.Node): node is BooleanKeyword { .api }

63

function isColonToken(node: ts.Node): node is ts.ColonToken { .api }

64

function isConstKeyword(node: ts.Node): node is ts.ConstKeyword { .api }

65

function isDeclareKeyword(node: ts.Node): node is ts.DeclareKeyword { .api }

66

function isDefaultKeyword(node: ts.Node): node is ts.DefaultKeyword { .api }

67

function isDotToken(node: ts.Node): node is ts.DotToken { .api }

68

function isEndOfFileToken(node: ts.Node): node is ts.EndOfFileToken { .api }

69

function isEqualsGreaterThanToken(node: ts.Node): node is ts.EqualsGreaterThanToken { .api }

70

function isEqualsToken(node: ts.Node): node is ts.EqualsToken { .api }

71

function isExclamationToken(node: ts.Node): node is ts.ExclamationToken { .api }

72

function isExportKeyword(node: ts.Node): node is ts.ExportKeyword { .api }

73

function isFalseKeyword(node: ts.Node): node is FalseKeyword { .api }

74

function isFalseLiteral(node: ts.Node): node is ts.FalseLiteral { .api }

75

function isImportExpression(node: ts.Node): node is ts.ImportExpression { .api }

76

function isImportKeyword(node: ts.Node): node is ImportKeyword { .api }

77

function isInKeyword(node: ts.Node): node is ts.InKeyword { .api }

78

function isJSDocText(node: ts.Node): node is ts.JSDocText { .api }

79

function isJsonMinusNumericLiteral(node: ts.Node): node is ts.JsonMinusNumericLiteral { .api }

80

function isNeverKeyword(node: ts.Node): node is NeverKeyword { .api }

81

function isNullKeyword(node: ts.Node): node is NullKeyword { .api }

82

function isNullLiteral(node: ts.Node): node is ts.NullLiteral { .api }

83

function isNumberKeyword(node: ts.Node): node is NumberKeyword { .api }

84

function isObjectKeyword(node: ts.Node): node is ObjectKeyword { .api }

85

function isOutKeyword(node: ts.Node): node is ts.OutKeyword { .api }

86

function isOverrideKeyword(node: ts.Node): node is ts.OverrideKeyword { .api }

87

function isPrivateKeyword(node: ts.Node): node is ts.PrivateKeyword { .api }

88

function isProtectedKeyword(node: ts.Node): node is ts.ProtectedKeyword { .api }

89

function isPublicKeyword(node: ts.Node): node is ts.PublicKeyword { .api }

90

function isQuestionDotToken(node: ts.Node): node is ts.QuestionDotToken { .api }

91

function isQuestionToken(node: ts.Node): node is ts.QuestionToken { .api }

92

function isReadonlyKeyword(node: ts.Node): node is ts.ReadonlyKeyword { .api }

93

function isStaticKeyword(node: ts.Node): node is ts.StaticKeyword { .api }

94

function isStringKeyword(node: ts.Node): node is StringKeyword { .api }

95

function isSuperExpression(node: ts.Node): node is ts.SuperExpression { .api }

96

function isSuperKeyword(node: ts.Node): node is SuperKeyword { .api }

97

function isSymbolKeyword(node: ts.Node): node is SymbolKeyword { .api }

98

function isSyntaxList(node: ts.Node): node is ts.SyntaxList { .api }

99

function isThisExpression(node: ts.Node): node is ts.ThisExpression { .api }

100

function isThisKeyword(node: ts.Node): node is ThisKeyword { .api }

101

function isTrueKeyword(node: ts.Node): node is TrueKeyword { .api }

102

function isTrueLiteral(node: ts.Node): node is ts.TrueLiteral { .api }

103

function isUndefinedKeyword(node: ts.Node): node is UndefinedKeyword { .api }

104

function isUnknownKeyword(node: ts.Node): node is UnknownKeyword { .api }

105

function isVoidKeyword(node: ts.Node): node is VoidKeyword { .api }

106

```

107

108

#### Keyword Node Types

109

110

The library defines specific types for TypeScript keywords:

111

112

```typescript

113

type AnyKeyword = ts.KeywordToken<ts.SyntaxKind.AnyKeyword>

114

type BigIntKeyword = ts.KeywordToken<ts.SyntaxKind.BigIntKeyword>

115

type BooleanKeyword = ts.KeywordToken<ts.SyntaxKind.BooleanKeyword>

116

type FalseKeyword = ts.KeywordToken<ts.SyntaxKind.FalseKeyword>

117

type ImportKeyword = ts.KeywordToken<ts.SyntaxKind.ImportKeyword>

118

type NeverKeyword = ts.KeywordToken<ts.SyntaxKind.NeverKeyword>

119

type NullKeyword = ts.KeywordToken<ts.SyntaxKind.NullKeyword>

120

type NumberKeyword = ts.KeywordToken<ts.SyntaxKind.NumberKeyword>

121

type ObjectKeyword = ts.KeywordToken<ts.SyntaxKind.ObjectKeyword>

122

type StringKeyword = ts.KeywordToken<ts.SyntaxKind.StringKeyword>

123

type SuperKeyword = ts.KeywordToken<ts.SyntaxKind.SuperKeyword>

124

type SymbolKeyword = ts.KeywordToken<ts.SyntaxKind.SymbolKeyword>

125

type ThisKeyword = ts.KeywordToken<ts.SyntaxKind.ThisKeyword>

126

type TrueKeyword = ts.KeywordToken<ts.SyntaxKind.TrueKeyword>

127

type UndefinedKeyword = ts.KeywordToken<ts.SyntaxKind.UndefinedKeyword>

128

type UnknownKeyword = ts.KeywordToken<ts.SyntaxKind.UnknownKeyword>

129

type VoidKeyword = ts.KeywordToken<ts.SyntaxKind.VoidKeyword>

130

```

131

132

### Union Type Guards

133

134

These functions test for nodes that match multiple possible types or have certain capabilities:

135

136

```typescript

137

function hasDecorators(node: ts.Node): node is ts.HasDecorators { .api }

138

function hasExpressionInitializer(node: ts.Node): node is ts.HasExpressionInitializer { .api }

139

function hasInitializer(node: ts.Node): node is ts.HasInitializer { .api }

140

function hasJSDoc(node: ts.Node): node is ts.HasJSDoc { .api }

141

function hasModifiers(node: ts.Node): node is ts.HasModifiers { .api }

142

function hasType(node: ts.Node): node is ts.HasType { .api }

143

function hasTypeArguments(node: ts.Node): node is ts.HasTypeArguments { .api }

144

function isAccessExpression(node: ts.Node): node is ts.AccessExpression { .api }

145

function isAccessibilityModifier(node: ts.Node): node is ts.AccessibilityModifier { .api }

146

function isAccessorDeclaration(node: ts.Node): node is ts.AccessorDeclaration { .api }

147

function isArrayBindingElement(node: ts.Node): node is ts.ArrayBindingElement { .api }

148

function isArrayBindingOrAssignmentPattern(node: ts.Node): node is ts.ArrayBindingOrAssignmentPattern { .api }

149

function isAssignmentPattern(node: ts.Node): node is ts.AssignmentPattern { .api }

150

function isBindingOrAssignmentElementRestIndicator(node: ts.Node): node is ts.BindingOrAssignmentElementRestIndicator { .api }

151

function isBindingOrAssignmentElementTarget(node: ts.Node): node is ts.BindingOrAssignmentElementTarget { .api }

152

function isBindingOrAssignmentPattern(node: ts.Node): node is ts.BindingOrAssignmentPattern { .api }

153

function isBindingPattern(node: ts.Node): node is ts.BindingPattern { .api }

154

function isBlockLike(node: ts.Node): node is ts.BlockLike { .api }

155

function isBooleanLiteral(node: ts.Node): node is ts.BooleanLiteral { .api }

156

function isClassLikeDeclaration(node: ts.Node): node is ts.ClassLikeDeclaration { .api }

157

function isClassMemberModifier(node: ts.Node): node is ts.ClassMemberModifier { .api }

158

function isDeclarationName(node: ts.Node): node is ts.DeclarationName { .api }

159

function isDeclarationWithTypeParameterChildren(node: ts.Node): node is ts.DeclarationWithTypeParameterChildren { .api }

160

function isDeclarationWithTypeParameters(node: ts.Node): node is ts.DeclarationWithTypeParameters { .api }

161

function isDestructuringPattern(node: ts.Node): node is ts.DestructuringPattern { .api }

162

function isEntityNameExpression(node: ts.Node): node is ts.EntityNameExpression { .api }

163

function isEntityNameOrEntityNameExpression(node: ts.Node): node is ts.EntityNameOrEntityNameExpression { .api }

164

function isForInOrOfStatement(node: ts.Node): node is ts.ForInOrOfStatement { .api }

165

function isFunctionLikeDeclaration(node: ts.Node): node is ts.FunctionLikeDeclaration { .api }

166

function isJSDocComment(node: ts.Node): node is ts.JSDocComment { .api }

167

function isJSDocNamespaceBody(node: ts.Node): node is ts.JSDocNamespaceBody { .api }

168

function isJSDocTypeReferencingNode(node: ts.Node): node is ts.JSDocTypeReferencingNode { .api }

169

function isJsonObjectExpression(node: ts.Node): node is ts.JsonObjectExpression { .api }

170

function isJsxAttributeLike(node: ts.Node): node is ts.JsxAttributeLike { .api }

171

function isJsxAttributeValue(node: ts.Node): node is ts.JsxAttributeValue { .api }

172

function isJsxChild(node: ts.Node): node is ts.JsxChild { .api }

173

function isJsxTagNameExpression(node: ts.Node): node is ts.JsxTagNameExpression { .api }

174

function isLiteralToken(node: ts.Node): node is ts.LiteralToken { .api }

175

function isModuleBody(node: ts.Node): node is ts.ModuleBody { .api }

176

function isModuleName(node: ts.Node): node is ts.ModuleName { .api }

177

function isModuleReference(node: ts.Node): node is ts.ModuleReference { .api }

178

function isNamedImportBindings(node: ts.Node): node is ts.NamedImportBindings { .api }

179

function isNamedImportsOrExports(node: ts.Node): node is ts.NamedImportsOrExports { .api }

180

function isNamespaceBody(node: ts.Node): node is ts.NamespaceBody { .api }

181

function isObjectBindingOrAssignmentElement(node: ts.Node): node is ts.ObjectBindingOrAssignmentElement { .api }

182

function isObjectBindingOrAssignmentPattern(node: ts.Node): node is ts.ObjectBindingOrAssignmentPattern { .api }

183

function isObjectTypeDeclaration(node: ts.Node): node is ts.ObjectTypeDeclaration { .api }

184

function isParameterPropertyModifier(node: ts.Node): node is ts.ParameterPropertyModifier { .api }

185

function isPropertyNameLiteral(node: ts.Node): node is ts.PropertyNameLiteral { .api }

186

function isPseudoLiteralToken(node: ts.Node): node is ts.PseudoLiteralToken { .api }

187

function isSignatureDeclaration(node: ts.Node): node is ts.SignatureDeclaration { .api }

188

function isSuperProperty(node: ts.Node): node is ts.SuperProperty { .api }

189

function isTypeOnlyCompatibleAliasDeclaration(node: ts.Node): node is ts.TypeOnlyCompatibleAliasDeclaration { .api }

190

function isTypeReferenceType(node: ts.Node): node is ts.TypeReferenceType { .api }

191

function isUnionOrIntersectionTypeNode(node: ts.Node): node is ts.UnionOrIntersectionTypeNode { .api }

192

function isVariableLikeDeclaration(node: ts.Node): node is ts.VariableLikeDeclaration { .api }

193

```

194

195

#### Usage Examples

196

197

```typescript

198

import { isAccessExpression, hasModifiers, isClassLikeDeclaration } from 'ts-api-utils';

199

200

// Check if a node is any kind of access expression

201

if (isAccessExpression(node)) {

202

// node is either PropertyAccessExpression or ElementAccessExpression

203

console.log('Accessing:', node.expression.getText());

204

}

205

206

// Check if a declaration has modifiers

207

if (hasModifiers(declaration)) {

208

// declaration has a modifiers array

209

const modifiers = declaration.modifiers?.map(mod => mod.getText()) ?? [];

210

console.log('Modifiers:', modifiers);

211

}

212

213

// Check if it's a class-like declaration

214

if (isClassLikeDeclaration(node)) {

215

// node is ClassDeclaration or ClassExpression

216

console.log('Class name:', node.name?.getText());

217

}

218

```

219

220

### Compound Type Guards

221

222

These functions test for more complex node patterns and combinations:

223

224

```typescript

225

function isConstAssertionExpression(node: ts.AssertionExpression): node is ConstAssertionExpression { .api }

226

function isIterationStatement(node: ts.Node): node is ts.IterationStatement { .api }

227

function isJSDocNamespaceDeclaration(node: ts.Node): node is ts.JSDocNamespaceDeclaration { .api }

228

function isJsxTagNamePropertyAccess(node: ts.Node): node is ts.JsxTagNamePropertyAccess { .api }

229

function isNamedDeclarationWithName(node: ts.Declaration): node is NamedDeclarationWithName { .api }

230

function isNamespaceDeclaration(node: ts.Node): node is ts.NamespaceDeclaration { .api }

231

function isNumericOrStringLikeLiteral(node: ts.Node): node is NumericOrStringLikeLiteral { .api }

232

function isPropertyAccessEntityNameExpression(node: ts.Node): node is ts.PropertyAccessEntityNameExpression { .api }

233

function isSuperElementAccessExpression(node: ts.Node): node is ts.SuperElementAccessExpression { .api }

234

function isSuperPropertyAccessExpression(node: ts.Node): node is ts.SuperPropertyAccessExpression { .api }

235

```

236

237

#### Compound Node Types

238

239

The library defines several compound node types for complex patterns:

240

241

```typescript

242

interface ConstAssertionExpression extends ts.AssertionExpression {

243

type: ts.TypeReferenceNode;

244

typeName: ConstAssertionIdentifier;

245

}

246

247

interface ConstAssertionIdentifier extends ts.Identifier {

248

escapedText: "const" & ts.__String;

249

}

250

251

interface NamedDeclarationWithName extends ts.NamedDeclaration {

252

name: ts.DeclarationName;

253

}

254

255

type NumericOrStringLikeLiteral = ts.NumericLiteral | ts.StringLiteralLike

256

```

257

258

#### Usage Examples

259

260

```typescript

261

import {

262

isConstAssertionExpression,

263

isIterationStatement,

264

isNamedDeclarationWithName

265

} from 'ts-api-utils';

266

267

// Check for const assertions like "as const"

268

if (ts.isAssertionExpression(node) && isConstAssertionExpression(node)) {

269

console.log('Found const assertion');

270

}

271

272

// Check for any kind of loop

273

if (isIterationStatement(node)) {

274

// node is do, for, for-in, for-of, or while statement

275

console.log('Found loop statement');

276

}

277

278

// Safely access names on declarations

279

if (isNamedDeclarationWithName(declaration)) {

280

// declaration.name is guaranteed to exist

281

console.log('Declaration name:', declaration.name.getText());

282

}

283

```

284

285

## Node Access Utilities

286

287

The access utilities help determine how expressions are being used in their context - whether they're being read from, written to, or deleted.

288

289

### AccessKind Enum

290

291

```typescript

292

enum AccessKind {

293

None = 0,

294

Read = 1,

295

Write = 2,

296

Delete = 4,

297

ReadWrite = 3 // Read | Write

298

}

299

```

300

301

### Access Detection

302

303

```typescript { .api }

304

function getAccessKind(node: ts.Expression): AccessKind;

305

```

306

307

This function analyzes an expression's context to determine what operations are being performed on it.

308

309

### Additional Node Utilities

310

311

```typescript { .api }

312

function isBindableObjectDefinePropertyCall(node: ts.CallExpression): boolean;

313

function isInConstContext(

314

node: ts.PropertyAssignment | ts.ShorthandPropertyAssignment,

315

typeChecker: ts.TypeChecker

316

): boolean;

317

```

318

319

The `isBindableObjectDefinePropertyCall` function determines if a call expression represents a statically analyzable `Object.defineProperty` call. The `isInConstContext` function detects if property assignments are affected by const assertion contexts.

320

321

#### Usage Examples

322

323

```typescript

324

import { getAccessKind, AccessKind } from 'ts-api-utils';

325

326

declare const expression: ts.Expression;

327

328

const access = getAccessKind(expression);

329

330

if (access & AccessKind.Read) {

331

console.log('Expression is being read');

332

}

333

334

if (access & AccessKind.Write) {

335

console.log('Expression is being written to');

336

}

337

338

if (access & AccessKind.Delete) {

339

console.log('Expression is being deleted');

340

}

341

342

// Check for specific access patterns

343

switch (access) {

344

case AccessKind.Read:

345

console.log('Read-only access: const x = expression');

346

break;

347

case AccessKind.Write:

348

console.log('Write-only access: expression = value');

349

break;

350

case AccessKind.ReadWrite:

351

console.log('Read-write access: expression++');

352

break;

353

case AccessKind.Delete:

354

console.log('Delete access: delete expression');

355

break;

356

case AccessKind.None:

357

console.log('No access (e.g., in type position)');

358

break;

359

}

360

```

361

362

#### Access Context Examples

363

364

Different contexts produce different access kinds:

365

366

```typescript

367

// Read access

368

console.log(variable); // AccessKind.Read

369

const x = obj.property; // AccessKind.Read (obj.property)

370

if (condition) { } // AccessKind.Read (condition)

371

372

// Write access

373

variable = 5; // AccessKind.Write

374

obj.property = value; // AccessKind.Write (obj.property)

375

const [a, b] = array; // AccessKind.Write (a, b in destructuring)

376

377

// Read-write access

378

variable++; // AccessKind.ReadWrite

379

++variable; // AccessKind.ReadWrite

380

variable += 10; // AccessKind.ReadWrite

381

382

// Delete access

383

delete obj.property; // AccessKind.Delete (obj.property)

384

385

// No access

386

type T = typeof variable; // AccessKind.None (in type position)

387

```

388

389

## Node Flag Utilities

390

391

Flag utilities test for various TypeScript compiler flags set on nodes, types, and symbols.

392

393

```typescript

394

function isModifierFlagSet(node: ts.Declaration, flag: ts.ModifierFlags): boolean { .api }

395

function isNodeFlagSet(node: ts.Node, flag: ts.NodeFlags): boolean { .api }

396

function isObjectFlagSet(objectType: ts.ObjectType, flag: ts.ObjectFlags): boolean { .api }

397

function isSymbolFlagSet(symbol: ts.Symbol, flag: ts.SymbolFlags): boolean { .api }

398

function isTypeFlagSet(type: ts.Type, flag: ts.TypeFlags): boolean { .api }

399

```

400

401

#### Usage Examples

402

403

```typescript

404

import {

405

isModifierFlagSet,

406

isNodeFlagSet,

407

isSymbolFlagSet,

408

isTypeFlagSet

409

} from 'ts-api-utils';

410

import ts from 'typescript';

411

412

// Check for abstract modifier

413

if (isModifierFlagSet(declaration, ts.ModifierFlags.Abstract)) {

414

console.log('Declaration is abstract');

415

}

416

417

// Check for await context

418

if (isNodeFlagSet(node, ts.NodeFlags.AwaitContext)) {

419

console.log('Node is in await context');

420

}

421

422

// Check symbol properties

423

if (isSymbolFlagSet(symbol, ts.SymbolFlags.Function)) {

424

console.log('Symbol represents a function');

425

}

426

427

// Check type properties

428

if (isTypeFlagSet(type, ts.TypeFlags.Union)) {

429

console.log('Type is a union type');

430

}

431

```

432

433

## Node Utilities

434

435

Additional helper functions for working with nodes in specific contexts.

436

437

### Internal Utilities

438

439

These utilities are used internally by the library but may be useful for advanced use cases:

440

441

```typescript

442

function isBindableObjectDefinePropertyCall(node: ts.CallExpression): boolean { .api }

443

function isInConstContext(

444

node: ts.PropertyAssignment | ts.ShorthandPropertyAssignment,

445

typeChecker: ts.TypeChecker

446

): boolean { .api }

447

```

448

449

#### Usage Examples

450

451

```typescript

452

import { isBindableObjectDefinePropertyCall, isInConstContext } from 'ts-api-utils';

453

454

// Check for statically analyzable Object.defineProperty calls

455

if (ts.isCallExpression(node) && isBindableObjectDefinePropertyCall(node)) {

456

console.log('Found Object.defineProperty call that can be analyzed');

457

}

458

459

// Check if property is in a const context

460

if (isInConstContext(propertyAssignment, typeChecker)) {

461

console.log('Property assignment is in const context (literal type)');

462

}

463

```

464

465

## Integration with Other Modules

466

467

Node Analysis utilities work seamlessly with other ts-api-utils modules:

468

469

### With Type Analysis

470

471

```typescript

472

import { isVariableDeclaration, isSignatureDeclaration } from 'ts-api-utils';

473

import { getCallSignaturesOfType } from 'ts-api-utils';

474

475

if (isVariableDeclaration(node) && node.type) {

476

const type = typeChecker.getTypeFromTypeNode(node.type);

477

const signatures = getCallSignaturesOfType(type);

478

479

if (signatures.length > 0) {

480

console.log('Variable has callable type');

481

}

482

}

483

```

484

485

### With Usage Analysis

486

487

```typescript

488

import { isVariableDeclaration, collectVariableUsage } from 'ts-api-utils';

489

490

const usage = collectVariableUsage(sourceFile);

491

492

for (const [identifier, info] of usage) {

493

if (info.declarations.some(decl =>

494

isVariableDeclaration(decl.parent) &&

495

hasModifiers(decl.parent.parent)

496

)) {

497

console.log('Variable declared with modifiers:', identifier.text);

498

}

499

}

500

```

501

502

## Best Practices

503

504

### Performance Considerations

505

506

1. **Use specific type guards**: Prefer specific guards like `isPropertyAccessExpression` over generic checks

507

2. **Cache results**: Type guard results don't change for a given node, so cache expensive checks

508

3. **Traverse efficiently**: Use parent/child relationships rather than re-searching the AST

509

510

### Type Safety

511

512

1. **Always use type guards**: Never cast nodes directly - use type guards for safe narrowing

513

2. **Check parent context**: Many node types are only meaningful in specific parent contexts

514

3. **Handle undefined cases**: Many node properties are optional - always check for existence

515

516

### Common Patterns

517

518

```typescript

519

// Safe property access pattern

520

function getPropertyName(node: ts.Node): string | undefined {

521

if (ts.isPropertyAccessExpression(node)) {

522

return node.name.text;

523

}

524

if (ts.isElementAccessExpression(node) && ts.isStringLiteral(node.argumentExpression)) {

525

return node.argumentExpression.text;

526

}

527

return undefined;

528

}

529

530

// Safe modifier checking pattern

531

function hasPublicModifier(node: ts.Node): boolean {

532

return hasModifiers(node) &&

533

node.modifiers?.some(isPublicKeyword) === true;

534

}

535

536

// Access pattern analysis

537

function analyzeExpression(expr: ts.Expression): void {

538

const access = getAccessKind(expr);

539

540

if (access & AccessKind.Write) {

541

// Handle write operations

542

if (access & AccessKind.Read) {

543

console.log('Read-write operation (e.g., +=, ++)');

544

} else {

545

console.log('Pure write operation (e.g., assignment)');

546

}

547

} else if (access & AccessKind.Read) {

548

console.log('Read-only access');

549

} else if (access & AccessKind.Delete) {

550

console.log('Delete operation');

551

}

552

}

553

```

554

555

The Node Analysis module provides the foundation for all TypeScript AST manipulation and analysis. By combining type guards, access utilities, and flag checking, you can build sophisticated tools that understand and manipulate TypeScript code with confidence and type safety.