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

eslint-utils.mddocs/

0

# ESLint Integration Utilities

1

2

Rule creation, testing, and configuration utilities specifically designed for TypeScript ESLint rules. These utilities provide a complete toolkit for developing, testing, and configuring ESLint rules with full TypeScript integration.

3

4

## Capabilities

5

6

### Rule Creation

7

8

Utilities for creating TypeScript-aware ESLint rules with automatic documentation and type safety.

9

10

```typescript { .api }

11

/**

12

* Creates a rule creator function with automatic documentation URL generation

13

* @param urlCreator - Function that generates documentation URLs from rule names

14

* @returns Function for creating rules with automatic docs URLs

15

*/

16

function RuleCreator(urlCreator: (ruleName: string) => string): <

17

TOptions extends readonly unknown[],

18

TMessageIds extends string,

19

TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener

20

>(ruleDefinition: Readonly<TSESLint.RuleModule<TMessageIds, TOptions, TRuleListener>>) => TSESLint.RuleModule<TMessageIds, TOptions>;

21

22

/**

23

* Creates a rule without automatic documentation URL

24

* @param ruleDefinition - Rule definition object

25

* @returns ESLint rule module

26

*/

27

declare namespace RuleCreator {

28

function withoutDocs<

29

TOptions extends readonly unknown[],

30

TMessageIds extends string,

31

TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener

32

>(ruleDefinition: Readonly<TSESLint.RuleModule<TMessageIds, TOptions, TRuleListener>>): TSESLint.RuleModule<TMessageIds, TOptions>;

33

}

34

```

35

36

**Usage Example:**

37

38

```typescript

39

import { ESLintUtils, TSESLint } from "@typescript-eslint/experimental-utils";

40

41

const createRule = ESLintUtils.RuleCreator(

42

name => `https://typescript-eslint.io/rules/${name}`

43

);

44

45

const rule = createRule({

46

name: 'my-rule',

47

meta: {

48

type: 'problem',

49

messages: {

50

error: 'This is an error message'

51

},

52

schema: []

53

},

54

defaultOptions: [],

55

create(context: TSESLint.RuleContext<'error', []>) {

56

return {

57

FunctionDeclaration(node) {

58

context.report({

59

node,

60

messageId: 'error'

61

});

62

}

63

};

64

}

65

});

66

```

67

68

### Parser Services

69

70

Utilities for accessing TypeScript compiler services within ESLint rules.

71

72

```typescript { .api }

73

/**

74

* Retrieves TypeScript parser services from the rule context

75

* @param context - ESLint rule context

76

* @param allowWithoutFullTypeInformation - Whether to allow partial type information

77

* @returns Parser services object with TypeScript compiler access

78

* @throws Error if parser services are not available

79

*/

80

function getParserServices<TMessageIds extends string, TOptions extends readonly unknown[]>(

81

context: TSESLint.RuleContext<TMessageIds, TOptions>,

82

allowWithoutFullTypeInformation?: boolean

83

): TSESTree.ParserServices;

84

```

85

86

**Usage Example:**

87

88

```typescript

89

import { ESLintUtils, TSESTree } from "@typescript-eslint/experimental-utils";

90

91

const rule = ESLintUtils.RuleCreator(url => url)({

92

name: 'type-aware-rule',

93

meta: {

94

type: 'problem',

95

messages: { error: 'Type error' },

96

schema: []

97

},

98

defaultOptions: [],

99

create(context) {

100

const services = ESLintUtils.getParserServices(context);

101

const checker = services.program?.getTypeChecker();

102

103

return {

104

Identifier(node) {

105

if (checker) {

106

const tsNode = services.esTreeNodeToTSNodeMap.get(node);

107

const type = checker.getTypeAtLocation(tsNode);

108

// Use TypeScript type information

109

}

110

}

111

};

112

}

113

});

114

```

115

116

### Configuration Utilities

117

118

Utilities for handling rule options and configuration merging.

119

120

```typescript { .api }

121

/**

122

* Applies default options to user-provided options using deep merge

123

* @param defaultOptions - Default option values

124

* @param userOptions - User-provided options (can be null)

125

* @returns Merged options with defaults applied

126

*/

127

function applyDefault<TUser, TDefault>(

128

defaultOptions: Readonly<TDefault>,

129

userOptions: Readonly<TUser> | null

130

): TDefault;

131

132

/**

133

* Deep merges two objects, combining properties recursively

134

* @param first - First object to merge

135

* @param second - Second object to merge

136

* @returns Merged object with combined properties

137

*/

138

function deepMerge(first?: ObjectLike, second?: ObjectLike): Record<string, unknown>;

139

140

/**

141

* Type guard to check if a value is an object but not an array

142

* @param obj - Value to check

143

* @returns True if the value is an object (not array)

144

*/

145

function isObjectNotArray<T = Record<string, unknown>>(obj: unknown): obj is T;

146

147

interface ObjectLike {

148

[key: string]: unknown;

149

}

150

```

151

152

**Usage Example:**

153

154

```typescript

155

import { ESLintUtils } from "@typescript-eslint/experimental-utils";

156

157

interface MyRuleOptions {

158

checkTypes: boolean;

159

ignorePatterns: string[];

160

severity: 'error' | 'warn';

161

}

162

163

const defaultOptions: MyRuleOptions = {

164

checkTypes: true,

165

ignorePatterns: [],

166

severity: 'error'

167

};

168

169

const rule = ESLintUtils.RuleCreator(url => url)({

170

name: 'my-rule',

171

meta: {

172

type: 'problem',

173

messages: { error: 'Error' },

174

schema: [/* schema */]

175

},

176

defaultOptions: [defaultOptions],

177

create(context, [userOptions]) {

178

const options = ESLintUtils.applyDefault(defaultOptions, userOptions);

179

// options now has all properties with defaults applied

180

181

return {

182

// rule implementation

183

};

184

}

185

});

186

```

187

188

### Type Utilities

189

190

Type utilities for inferring types from rule definitions.

191

192

```typescript { .api }

193

/**

194

* Infers the options type from a rule module

195

*/

196

type InferOptionsTypeFromRule<T> = T extends TSESLint.RuleModule<string, infer TOptions, TSESLint.RuleListener> ? TOptions : unknown;

197

198

/**

199

* Infers the message IDs type from a rule module

200

*/

201

type InferMessageIdsTypeFromRule<T> = T extends TSESLint.RuleModule<infer TMessageIds, readonly unknown[], TSESLint.RuleListener> ? TMessageIds : string;

202

```

203

204

### Utility Functions

205

206

Helper functions for common rule development tasks.

207

208

```typescript { .api }

209

/**

210

* Non-null assertion with custom error message

211

* @param value - Value to check for null/undefined

212

* @param message - Error message if value is null/undefined

213

* @returns Non-null value

214

* @throws Error if value is null or undefined

215

*/

216

function nullThrows<T>(value: T | null | undefined, message: string): T;

217

218

/**

219

* Common error messages for nullThrows function

220

*/

221

const NullThrowsReasons: {

222

readonly MissingParent: "Expected node to have a parent.";

223

readonly MissingToken: "Expected to find a token.";

224

};

225

```

226

227

**Usage Example:**

228

229

```typescript

230

import { ESLintUtils, TSESTree } from "@typescript-eslint/experimental-utils";

231

232

function analyzeNode(node: TSESTree.Node): void {

233

const parent = ESLintUtils.nullThrows(

234

node.parent,

235

ESLintUtils.NullThrowsReasons.MissingParent

236

);

237

// parent is now guaranteed to be non-null

238

}

239

```

240

241

### Rule Testing

242

243

Enhanced rule testing utilities with TypeScript support and batch test processing.

244

245

```typescript { .api }

246

/**

247

* Enhanced ESLint rule tester with TypeScript support

248

*/

249

class RuleTester {

250

/**

251

* Creates a new rule tester with base configuration

252

* @param baseOptions - Base configuration for the rule tester

253

*/

254

constructor(baseOptions: TSESLint.RuleTesterConfig);

255

256

/**

257

* Runs tests for a specific rule with TypeScript support

258

* @param name - Name of the rule being tested

259

* @param rule - Rule module to test

260

* @param tests - Test cases (valid and invalid)

261

*/

262

run<TMessageIds extends string, TOptions extends readonly unknown[]>(

263

name: string,

264

rule: TSESLint.RuleModule<TMessageIds, TOptions>,

265

tests: TSESLint.RunTests<TMessageIds, TOptions>

266

): void;

267

268

/**

269

* Static cleanup function called after all tests

270

*/

271

static afterAll: (() => void) | undefined;

272

}

273

274

/**

275

* Template tag to mark code as "no format" for testing purposes

276

* @param raw - Template strings array

277

* @param keys - Template substitution values

278

* @returns Formatted string marked as no-format

279

*/

280

function noFormat(raw: TemplateStringsArray, ...keys: string[]): string;

281

282

/**

283

* Converts batch test cases into individual test cases

284

* @param test - Batch test configuration

285

* @returns Array of individual test cases

286

*/

287

function batchedSingleLineTests<TOptions extends readonly unknown[]>(test: {

288

code: string;

289

options?: TOptions;

290

skip?: boolean;

291

only?: boolean;

292

}): TSESLint.ValidTestCase<TOptions>[];

293

294

function batchedSingleLineTests<TMessageIds extends string, TOptions extends readonly unknown[]>(test: {

295

code: string;

296

options?: TOptions;

297

skip?: boolean;

298

only?: boolean;

299

errors: TestCaseError<TMessageIds>[];

300

output?: string | null;

301

}): TSESLint.InvalidTestCase<TMessageIds, TOptions>[];

302

303

interface TestCaseError<TMessageIds extends string> {

304

messageId: TMessageIds;

305

data?: Record<string, unknown>;

306

type?: string;

307

line?: number;

308

column?: number;

309

endLine?: number;

310

endColumn?: number;

311

suggestions?: SuggestionOutput<TMessageIds>[];

312

}

313

314

interface SuggestionOutput<TMessageIds extends string> {

315

messageId: TMessageIds;

316

data?: Record<string, unknown>;

317

output: string;

318

desc?: string;

319

}

320

```

321

322

**Usage Example:**

323

324

```typescript

325

import { ESLintUtils, TSESLint } from "@typescript-eslint/experimental-utils";

326

327

const ruleTester = new ESLintUtils.RuleTester({

328

parser: '@typescript-eslint/parser',

329

parserOptions: {

330

ecmaVersion: 2020,

331

sourceType: 'module',

332

project: './tsconfig.json'

333

}

334

});

335

336

const rule = ESLintUtils.RuleCreator(url => url)({

337

name: 'test-rule',

338

meta: {

339

type: 'problem',

340

messages: {

341

error: 'Found error in {{name}}'

342

},

343

schema: []

344

},

345

defaultOptions: [],

346

create(context) {

347

return {

348

FunctionDeclaration(node) {

349

context.report({

350

node,

351

messageId: 'error',

352

data: { name: node.id?.name || 'unknown' }

353

});

354

}

355

};

356

}

357

});

358

359

ruleTester.run('test-rule', rule, {

360

valid: [

361

'const x = 1;',

362

'class MyClass {}',

363

{

364

code: 'const arrow = () => {};',

365

parserOptions: { ecmaVersion: 6 }

366

}

367

],

368

invalid: [

369

{

370

code: 'function foo() {}',

371

errors: [{

372

messageId: 'error',

373

data: { name: 'foo' },

374

line: 1,

375

column: 1

376

}]

377

},

378

{

379

code: ESLintUtils.noFormat`

380

function bar() {

381

// some code

382

}

383

`,

384

errors: [{ messageId: 'error' }]

385

}

386

]

387

});

388

389

// Using batched tests

390

const batchTests = ESLintUtils.batchedSingleLineTests({

391

code: `

392

function a() {}

393

function b() {}

394

function c() {}

395

`,

396

errors: [

397

{ messageId: 'error', line: 2 },

398

{ messageId: 'error', line: 3 },

399

{ messageId: 'error', line: 4 }

400

]

401

});

402

```

403

404

### Dependency Constraints

405

406

Utilities for checking package version dependencies in tests.

407

408

```typescript { .api }

409

/**

410

* Checks if all specified dependency version constraints are satisfied

411

* @param dependencyConstraints - Object mapping package names to version constraints

412

* @returns True if all constraints are satisfied

413

*/

414

function satisfiesAllDependencyConstraints(

415

dependencyConstraints?: DependencyConstraint

416

): boolean;

417

418

/**

419

* Interface for specifying package version constraints

420

*/

421

interface DependencyConstraint {

422

[packageName: string]: VersionConstraint;

423

}

424

425

type VersionConstraint = string | Range | SemVer;

426

427

interface Range {

428

range: string;

429

raw: string;

430

loose: boolean;

431

includePrerelease: boolean;

432

test(version: string | SemVer): boolean;

433

intersects(range: Range): boolean;

434

}

435

436

interface SemVer {

437

version: string;

438

major: number;

439

minor: number;

440

patch: number;

441

prerelease: readonly (string | number)[];

442

build: readonly string[];

443

compare(other: string | SemVer): -1 | 0 | 1;

444

compareMain(other: string | SemVer): -1 | 0 | 1;

445

}

446

```

447

448

**Usage Example:**

449

450

```typescript

451

import { ESLintUtils } from "@typescript-eslint/experimental-utils";

452

453

// Check if TypeScript version meets requirements

454

const hasRequiredTypescript = ESLintUtils.satisfiesAllDependencyConstraints({

455

typescript: '>=4.0.0'

456

});

457

458

if (hasRequiredTypescript) {

459

// Run TypeScript-specific tests

460

} else {

461

// Skip tests that require newer TypeScript

462

}

463

```

464

465

## Test Case Types

466

467

```typescript { .api }

468

/**

469

* Valid test case configuration

470

*/

471

interface ValidTestCase<TOptions extends readonly unknown[]> {

472

/** Source code that should not trigger any errors */

473

code: string;

474

/** Rule options to use for this test */

475

options?: TOptions;

476

/** Filename for the test case */

477

filename?: string;

478

/** Parser options specific to this test */

479

parserOptions?: TSESLint.ParserOptions;

480

/** ESLint settings for this test */

481

settings?: Record<string, unknown>;

482

/** Parser to use (defaults to configured parser) */

483

parser?: string;

484

/** Global variables available in this test */

485

globals?: Record<string, boolean>;

486

/** Environment settings for this test */

487

env?: TSESLint.Linter.Environment;

488

/** Skip this test case */

489

skip?: boolean;

490

/** Run only this test case */

491

only?: boolean;

492

/** Dependency constraints for this test */

493

dependencyConstraints?: DependencyConstraint;

494

}

495

496

/**

497

* Invalid test case configuration

498

*/

499

interface InvalidTestCase<TMessageIds extends string, TOptions extends readonly unknown[]>

500

extends ValidTestCase<TOptions> {

501

/** Expected errors from this test case */

502

errors: TestCaseError<TMessageIds>[];

503

/** Expected output after fixes are applied */

504

output?: string | null;

505

}

506

507

/**

508

* Complete test suite for a rule

509

*/

510

interface RunTests<TMessageIds extends string, TOptions extends readonly unknown[]> {

511

/** Test cases that should not produce any errors */

512

valid: (string | ValidTestCase<TOptions>)[];

513

/** Test cases that should produce errors */

514

invalid: InvalidTestCase<TMessageIds, TOptions>[];

515

}

516

```

517

518

## Rule Tester Configuration

519

520

```typescript { .api }

521

/**

522

* Configuration for the ESLint rule tester

523

*/

524

interface RuleTesterConfig {

525

/** Parser to use for parsing test code */

526

parser?: string;

527

/** Parser options passed to the parser */

528

parserOptions?: TSESLint.ParserOptions;

529

/** Global variables available during testing */

530

globals?: Record<string, boolean>;

531

/** Environment settings */

532

env?: TSESLint.Linter.Environment;

533

/** ESLint settings object */

534

settings?: Record<string, unknown>;

535

/** Language options for the parser */

536

languageOptions?: {

537

ecmaVersion?: TSESLint.ParserOptions['ecmaVersion'];

538

sourceType?: TSESLint.ParserOptions['sourceType'];

539

globals?: Record<string, boolean>;

540

parser?: { parse(text: string, options?: any): any };

541

parserOptions?: Record<string, unknown>;

542

};

543

/** Linter options */

544

linterOptions?: {

545

noInlineConfig?: boolean;

546

reportUnusedDisableDirectives?: boolean;

547

};

548

}

549

```