or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

analysis.mddefinition-system.mdindex.mdreference-system.mdscope-management.mdscope-types.mdvariable-system.md

variable-system.mddocs/

0

# Variable System

1

2

The variable tracking system provides TypeScript-aware type context analysis and comprehensive definition tracking for all identifiers in analyzed code.

3

4

## Capabilities

5

6

### Variable Class

7

8

The main Variable class that represents variables with TypeScript type context awareness.

9

10

```typescript { .api }

11

/**

12

* A Variable represents a locally scoped identifier. These include arguments to functions.

13

*/

14

class Variable extends VariableBase {

15

/** Unique identifier for this variable instance */

16

readonly $id: number;

17

18

/** The variable name, as given in the source code */

19

readonly name: string;

20

21

/** The array of the definitions of this variable */

22

readonly defs: Definition[];

23

24

/** The array of Identifier nodes which define this variable */

25

readonly identifiers: TSESTree.Identifier[];

26

27

/** List of References of this variable in its defining scope and all nested scopes */

28

readonly references: Reference[];

29

30

/** The scope where this variable is defined */

31

readonly scope: Scope;

32

33

/** True if the variable is considered used for the purposes of no-unused-vars */

34

eslintUsed: boolean;

35

36

/** True if the variable is valid in a type context, false otherwise */

37

get isTypeVariable(): boolean;

38

39

/** True if the variable is valid in a value context, false otherwise */

40

get isValueVariable(): boolean;

41

}

42

```

43

44

### ESLint Compatible Variable

45

46

ESLint-compatible variable implementation for seamless integration with existing ESLint rules.

47

48

```typescript { .api }

49

/**

50

* ESLint-compatible variable that maintains compatibility with standard ESLint scope analysis

51

*/

52

class ESLintScopeVariable extends VariableBase {

53

readonly $id: number;

54

readonly name: string;

55

readonly defs: Definition[];

56

readonly identifiers: TSESTree.Identifier[];

57

readonly references: Reference[];

58

readonly scope: Scope;

59

eslintUsed: boolean;

60

61

/** If this key exists, this variable is a global variable added by ESLint */

62

writeable?: boolean;

63

64

/** Indicates if there are globals comment directives */

65

eslintExplicitGlobal?: boolean;

66

67

/** The configured value in config files */

68

eslintImplicitGlobalSetting?: 'readonly' | 'writable';

69

70

/** Global variable comments from source code */

71

eslintExplicitGlobalComments?: TSESTree.Comment[];

72

}

73

```

74

75

### Implicit Library Variable

76

77

Variables automatically defined by TypeScript library definitions.

78

79

```typescript { .api }

80

/**

81

* Variable representing TypeScript library definitions (e.g., console, window, etc.)

82

*/

83

class ImplicitLibVariable extends VariableBase {

84

readonly $id: number;

85

readonly name: string;

86

readonly defs: LibDefinition[];

87

readonly identifiers: TSESTree.Identifier[];

88

readonly references: Reference[];

89

readonly scope: Scope;

90

eslintUsed: boolean;

91

92

constructor(scope: Scope, name: string, options: ImplicitLibVariableOptions);

93

}

94

95

interface ImplicitLibVariableOptions {

96

readonly eslintImplicitGlobalSetting?: 'readonly' | 'writable';

97

readonly isTypeVariable?: boolean;

98

readonly isValueVariable?: boolean;

99

readonly writeable?: boolean;

100

}

101

102

interface LibDefinition {

103

libs: readonly LibDefinition[];

104

variables: readonly [string, ImplicitLibVariableOptions][];

105

}

106

```

107

108

### Variable Union Type

109

110

Union type representing all possible variable types in the system.

111

112

```typescript { .api }

113

type ScopeVariable = ESLintScopeVariable | Variable;

114

```

115

116

**Usage Examples:**

117

118

```typescript

119

import { analyze } from "@typescript-eslint/scope-manager";

120

import { parse } from "@typescript-eslint/parser";

121

122

const code = `

123

interface User {

124

name: string;

125

age: number;

126

}

127

128

function createUser(name: string, age: number): User {

129

const user: User = { name, age };

130

return user;

131

}

132

133

enum Status {

134

Active,

135

Inactive

136

}

137

`;

138

139

const ast = parse(code);

140

const scopeManager = analyze(ast, {

141

sourceType: 'module',

142

lib: ['es2020', 'dom']

143

});

144

145

// Analyze all variables

146

scopeManager.variables.forEach(variable => {

147

console.log(`Variable: ${variable.name}`);

148

console.log(` Type context: ${variable.isTypeVariable}`);

149

console.log(` Value context: ${variable.isValueVariable}`);

150

console.log(` Scope: ${variable.scope.type}`);

151

console.log(` Definitions: ${variable.defs.length}`);

152

console.log(` References: ${variable.references.length}`);

153

console.log(` ESLint used: ${variable.eslintUsed}`);

154

});

155

156

// Check type vs value variables

157

const typeOnlyVars = scopeManager.variables.filter(v =>

158

v.isTypeVariable && !v.isValueVariable

159

);

160

console.log('Type-only variables:', typeOnlyVars.map(v => v.name));

161

162

const valueOnlyVars = scopeManager.variables.filter(v =>

163

v.isValueVariable && !v.isTypeVariable

164

);

165

console.log('Value-only variables:', valueOnlyVars.map(v => v.name));

166

167

const dualContextVars = scopeManager.variables.filter(v =>

168

v.isTypeVariable && v.isValueVariable

169

);

170

console.log('Dual-context variables:', dualContextVars.map(v => v.name));

171

```

172

173

### Variable Context Analysis

174

175

TypeScript variables can exist in different contexts:

176

177

```typescript

178

// Examples of different variable contexts

179

180

// Type-only context

181

interface Point { x: number; y: number } // 'Point' is type-only

182

type StringOrNumber = string | number; // 'StringOrNumber' is type-only

183

184

// Value-only context

185

const message = "Hello"; // 'message' is value-only

186

function greet() { } // 'greet' is value-only

187

188

// Dual context (both type and value)

189

class Rectangle { // 'Rectangle' is both type and value

190

width: number;

191

height: number;

192

}

193

194

enum Color { // 'Color' is both type and value

195

Red, Green, Blue

196

}

197

198

namespace Utils { // 'Utils' is both type and value

199

export const helper = () => {};

200

}

201

202

// Usage analysis

203

const scopeManager = analyze(ast);

204

scopeManager.variables.forEach(variable => {

205

if (variable.isTypeVariable && variable.isValueVariable) {

206

console.log(`${variable.name} can be used as both type and value`);

207

} else if (variable.isTypeVariable) {

208

console.log(`${variable.name} can only be used as a type`);

209

} else if (variable.isValueVariable) {

210

console.log(`${variable.name} can only be used as a value`);

211

}

212

});

213

```

214

215

### Variable Definition Analysis

216

217

Variables can have multiple definitions from various sources:

218

219

```typescript

220

// Analyze variable definitions

221

scopeManager.variables.forEach(variable => {

222

console.log(`\nVariable: ${variable.name}`);

223

224

variable.defs.forEach((def, index) => {

225

console.log(` Definition ${index}:`);

226

console.log(` Type: ${def.type}`);

227

console.log(` Node: ${def.node.type}`);

228

console.log(` Is type definition: ${def.isTypeDefinition}`);

229

console.log(` Is variable definition: ${def.isVariableDefinition}`);

230

});

231

232

// Check if variable is redeclared

233

if (variable.defs.length > 1) {

234

console.log(` ⚠️ Variable ${variable.name} is redeclared`);

235

}

236

237

// Analyze identifiers

238

console.log(` Identifier nodes: ${variable.identifiers.length}`);

239

variable.identifiers.forEach((id, index) => {

240

console.log(` ${index}: ${id.name} at line ${id.loc?.start.line}`);

241

});

242

});

243

```

244

245

### Variable Reference Analysis

246

247

Track how variables are used throughout the code:

248

249

```typescript

250

// Analyze variable references

251

scopeManager.variables.forEach(variable => {

252

console.log(`\nVariable: ${variable.name}`);

253

console.log(`References: ${variable.references.length}`);

254

255

variable.references.forEach((ref, index) => {

256

console.log(` Reference ${index}:`);

257

console.log(` From scope: ${ref.from.type}`);

258

console.log(` Is read: ${ref.isRead()}`);

259

console.log(` Is write: ${ref.isWrite()}`);

260

console.log(` Resolved: ${ref.resolved ? 'Yes' : 'No'}`);

261

262

if (ref.writeExpr) {

263

console.log(` Write expression: ${ref.writeExpr.type}`);

264

}

265

});

266

267

// Analyze usage patterns

268

const reads = variable.references.filter(ref => ref.isRead());

269

const writes = variable.references.filter(ref => ref.isWrite());

270

271

console.log(` Read operations: ${reads.length}`);

272

console.log(` Write operations: ${writes.length}`);

273

274

if (writes.length === 0 && variable.defs.length > 0) {

275

console.log(` ⚠️ Variable ${variable.name} is never modified`);

276

}

277

278

if (reads.length === 0) {

279

console.log(` ⚠️ Variable ${variable.name} is never read`);

280

}

281

});

282

```

283

284

### Implicit Library Variables

285

286

TypeScript library definitions create implicit variables:

287

288

```typescript

289

// Analyze implicit library variables

290

const globalScope = scopeManager.globalScope;

291

if (globalScope) {

292

const implicitVars = globalScope.variables.filter(v =>

293

v instanceof ImplicitLibVariable

294

);

295

296

console.log('Implicit library variables:');

297

implicitVars.forEach(variable => {

298

const implicitVar = variable as ImplicitLibVariable;

299

console.log(` ${implicitVar.name}`);

300

console.log(` Type variable: ${implicitVar.isTypeVariable}`);

301

console.log(` Value variable: ${implicitVar.isValueVariable}`);

302

console.log(` Writable: ${implicitVar.writeable}`);

303

});

304

}

305

306

// Common implicit variables from TypeScript libs:

307

// - console (from lib.dom.d.ts or lib.node.d.ts)

308

// - window, document (from lib.dom.d.ts)

309

// - process, Buffer (from @types/node)

310

// - Promise, Array, Object (from lib.es*.d.ts)

311

```

312

313

### Variable Scope Relationships

314

315

Understanding how variables relate to their scopes:

316

317

```typescript

318

// Analyze variable-scope relationships

319

scopeManager.scopes.forEach(scope => {

320

console.log(`\nScope: ${scope.type}`);

321

console.log(`Variables defined in this scope: ${scope.set.size}`);

322

323

// Variables defined directly in this scope

324

scope.set.forEach((variable, name) => {

325

console.log(` Owns: ${name}`);

326

});

327

328

// All variables accessible in this scope

329

console.log(`Total accessible variables: ${scope.variables.length}`);

330

331

// Check variable scope chain

332

scope.variables.forEach(variable => {

333

if (variable.scope === scope) {

334

console.log(` Local: ${variable.name}`);

335

} else {

336

console.log(` Inherited: ${variable.name} (from ${variable.scope.type})`);

337

}

338

});

339

});

340

```