or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ast-utils.mdeslint-utils.mdindex.mdjson-schema.mdts-eslint.mdts-utils.md

ts-eslint.mddocs/

0

# TSESLint Types

1

2

The `TSESLint` namespace provides TypeScript-enhanced ESLint types, classes, and interfaces for building type-safe ESLint rules and configurations.

3

4

## Import

5

6

```typescript { .api }

7

import { TSESLint } from '@typescript-eslint/utils';

8

```

9

10

## AST Types

11

12

### AST Namespace

13

14

```typescript { .api }

15

namespace AST {

16

// Token types from TSESTree

17

type TokenType = TSESTree.Token;

18

type Token = TSESTree.Token;

19

20

// Source location and range

21

type SourceLocation = TSESTree.SourceLocation;

22

type Range = TSESTree.Range;

23

}

24

25

// Usage examples

26

function processToken(token: TSESLint.AST.Token) {

27

const location: TSESLint.AST.SourceLocation = token.loc;

28

const range: TSESLint.AST.Range = token.range;

29

}

30

```

31

32

## Configuration Types

33

34

### Shared Configuration

35

36

```typescript { .api }

37

namespace SharedConfig {

38

// Global configuration settings

39

interface GlobalsConfig {

40

[name: string]: boolean | 'readonly' | 'writable' | 'off';

41

}

42

43

// Language options

44

interface LanguageOptions {

45

ecmaVersion?: EcmaVersion;

46

sourceType?: 'script' | 'module';

47

globals?: GlobalsConfig;

48

parser?: Parser.LooseParserModule;

49

parserOptions?: ParserOptions;

50

}

51

52

// Linter options

53

interface LinterOptions {

54

noInlineConfig?: boolean;

55

reportUnusedDisableDirectives?: boolean | 'error' | 'warn' | 'off';

56

}

57

58

// Rule configuration levels

59

type RuleLevel = 'off' | 'warn' | 'error' | 0 | 1 | 2;

60

61

// Rule configuration entry

62

type RuleEntry<Options extends readonly unknown[] = readonly unknown[]> =

63

| RuleLevel

64

| [RuleLevel]

65

| [RuleLevel, ...Options];

66

67

// Rules configuration

68

type RulesRecord = Record<string, RuleEntry>;

69

}

70

```

71

72

### Classic Configuration

73

74

```typescript { .api }

75

namespace ClassicConfig {

76

// Environment configuration

77

interface EnvironmentConfig {

78

[name: string]: boolean;

79

}

80

81

// Classic ESLint configuration format

82

interface Config {

83

// Rule and environment settings

84

rules?: SharedConfig.RulesRecord;

85

env?: EnvironmentConfig;

86

globals?: SharedConfig.GlobalsConfig;

87

88

// Parser settings

89

parser?: string;

90

parserOptions?: ParserOptions;

91

92

// Plugin and extension settings

93

plugins?: string[];

94

extends?: string | string[];

95

96

// File pattern settings

97

files?: string | string[];

98

excludedFiles?: string | string[];

99

100

// Inheritance settings

101

root?: boolean;

102

ignorePatterns?: string | string[];

103

104

// Override configurations

105

overrides?: ConfigOverride[];

106

107

// Processor settings

108

processor?: string;

109

110

// Settings object

111

settings?: Record<string, unknown>;

112

}

113

114

// Configuration override

115

interface ConfigOverride extends Config {

116

files: string | string[];

117

excludedFiles?: string | string[];

118

}

119

}

120

```

121

122

### Flat Configuration

123

124

```typescript { .api }

125

namespace FlatConfig {

126

// Flat configuration format (ESLint 9+)

127

interface Config {

128

// File matching

129

files?: string[];

130

ignores?: string[];

131

132

// Language and linter options

133

languageOptions?: SharedConfig.LanguageOptions;

134

linterOptions?: SharedConfig.LinterOptions;

135

136

// Processor configuration

137

processor?: string | Processor.ProcessorModule;

138

139

// Plugin configuration

140

plugins?: Record<string, Plugin>;

141

142

// Rule configuration

143

rules?: SharedConfig.RulesRecord;

144

145

// Settings

146

settings?: Record<string, unknown>;

147

148

// Name for configuration identification

149

name?: string;

150

}

151

152

// Plugin interface

153

interface Plugin {

154

configs?: Record<string, Config | Config[]>;

155

environments?: Record<string, SharedConfig.GlobalsConfig>;

156

processors?: Record<string, Processor.ProcessorModule>;

157

rules?: Record<string, Rule.RuleModule>;

158

159

// Metadata

160

meta?: {

161

name?: string;

162

version?: string;

163

};

164

}

165

166

// Configuration array

167

type ConfigArray = Config[];

168

}

169

```

170

171

## Rule System

172

173

### Rule Module Interface

174

175

```typescript { .api }

176

interface RuleModule<

177

MessageIds extends string = string,

178

Options extends readonly unknown[] = readonly unknown[],

179

Docs extends Record<string, unknown> = Record<string, unknown>

180

> {

181

// Rule metadata

182

meta: RuleMetaData<MessageIds, Docs, Options>;

183

184

// Rule implementation

185

create: (

186

context: RuleContext<MessageIds, Options>

187

) => RuleListener;

188

189

// Default options

190

defaultOptions?: Options;

191

}

192

193

// Rule metadata

194

interface RuleMetaData<

195

MessageIds extends string,

196

Docs extends Record<string, unknown>,

197

Options extends readonly unknown[]

198

> {

199

type: 'problem' | 'suggestion' | 'layout';

200

201

// Documentation

202

docs?: RuleMetaDataDocs<Docs>;

203

204

// Messages for reporting

205

messages: Record<MessageIds, string>;

206

207

// Configuration schema

208

schema: JSONSchema4 | readonly JSONSchema4[];

209

210

// Fixability

211

fixable?: 'code' | 'whitespace';

212

hasSuggestions?: boolean;

213

214

// Deprecation

215

deprecated?: boolean;

216

replacedBy?: readonly string[];

217

}

218

219

// Rule documentation metadata

220

interface RuleMetaDataDocs<Docs extends Record<string, unknown>>

221

extends Docs {

222

description?: string;

223

url?: string;

224

}

225

```

226

227

### Rule Context

228

229

```typescript { .api }

230

interface RuleContext<

231

MessageIds extends string = string,

232

Options extends readonly unknown[] = readonly unknown[]

233

> {

234

// Rule identification

235

id: string;

236

options: Options;

237

settings: Record<string, unknown>;

238

parserOptions: ParserOptions;

239

240

// File information

241

filename: string;

242

physicalFilename: string;

243

cwd: string;

244

245

// Source code access

246

sourceCode: SourceCode;

247

248

// Legacy methods (deprecated)

249

getAncestors(): TSESTree.Node[];

250

getDeclaredVariables(node: TSESTree.Node): Scope.Variable[];

251

getFilename(): string;

252

getPhysicalFilename(): string;

253

getCwd(): string;

254

getScope(): Scope.Scope;

255

getSourceCode(): SourceCode;

256

markVariableAsUsed(name: string): boolean;

257

258

// Reporting

259

report(descriptor: ReportDescriptor<MessageIds>): void;

260

}

261

```

262

263

### Rule Listener

264

265

```typescript { .api }

266

// Rule visitor methods

267

type RuleListener = {

268

[K in TSESTree.Node as K['type']]?: (node: K) => void;

269

} & {

270

[K in TSESTree.Node as `${K['type']}:exit`]?: (node: K) => void;

271

} & {

272

[K in `${TSESTree.Node['type']}:exit`]?: (node: TSESTree.Node) => void;

273

} & {

274

Program?: (node: TSESTree.Program) => void;

275

'Program:exit'?: (node: TSESTree.Program) => void;

276

};

277

278

// Usage example

279

const ruleListener: TSESLint.RuleListener = {

280

// Enter node handlers

281

FunctionDeclaration(node) {

282

// Handle function declarations

283

},

284

285

CallExpression(node) {

286

// Handle call expressions

287

},

288

289

// Exit node handlers

290

'FunctionDeclaration:exit'(node) {

291

// Handle leaving function declarations

292

},

293

294

// Program-level handlers

295

Program(node) {

296

// Handle program start

297

},

298

299

'Program:exit'(node) {

300

// Handle program end

301

}

302

};

303

```

304

305

### Report Descriptor

306

307

```typescript { .api }

308

interface ReportDescriptor<MessageIds extends string> {

309

// Target specification (one required)

310

node?: TSESTree.Node;

311

loc?: TSESTree.SourceLocation | TSESTree.LineAndColumnData;

312

313

// Message specification

314

messageId: MessageIds;

315

data?: Record<string, unknown>;

316

317

// Fix and suggestions

318

fix?: ReportFixFunction;

319

suggest?: SuggestionReportDescriptor<MessageIds>[];

320

}

321

322

// Fix function type

323

type ReportFixFunction = (fixer: RuleFixer) => null | RuleFix | readonly RuleFix[];

324

325

// Suggestion descriptor

326

interface SuggestionReportDescriptor<MessageIds extends string> {

327

messageId: MessageIds;

328

data?: Record<string, unknown>;

329

fix: ReportFixFunction;

330

}

331

332

// Usage example

333

context.report({

334

node,

335

messageId: 'unexpectedToken',

336

data: { token: 'function' },

337

fix: (fixer) => fixer.replaceText(node, 'const'),

338

suggest: [

339

{

340

messageId: 'useConst',

341

fix: (fixer) => fixer.replaceText(node, 'const')

342

},

343

{

344

messageId: 'useLet',

345

fix: (fixer) => fixer.replaceText(node, 'let')

346

}

347

]

348

});

349

```

350

351

### Rule Fixer

352

353

```typescript { .api }

354

interface RuleFixer {

355

// Text insertion

356

insertTextAfter(nodeOrToken: TSESTree.Node | TSESTree.Token, text: string): RuleFix;

357

insertTextBefore(nodeOrToken: TSESTree.Node | TSESTree.Token, text: string): RuleFix;

358

insertTextAfterRange(range: TSESTree.Range, text: string): RuleFix;

359

insertTextBeforeRange(range: TSESTree.Range, text: string): RuleFix;

360

361

// Text replacement

362

replaceText(nodeOrToken: TSESTree.Node | TSESTree.Token, text: string): RuleFix;

363

replaceTextRange(range: TSESTree.Range, text: string): RuleFix;

364

365

// Text removal

366

remove(nodeOrToken: TSESTree.Node | TSESTree.Token): RuleFix;

367

removeRange(range: TSESTree.Range): RuleFix;

368

}

369

370

// Rule fix object

371

interface RuleFix {

372

range: TSESTree.Range;

373

text: string;

374

}

375

```

376

377

## ESLint Classes

378

379

### ESLint Main Class

380

381

```typescript { .api }

382

// Main ESLint class (alias for FlatESLint)

383

declare const ESLint: typeof FlatESLint;

384

385

class FlatESLint {

386

constructor(options?: FlatESLint.Options);

387

388

// Linting methods

389

lintFiles(patterns: string | string[]): Promise<FlatESLint.LintResult[]>;

390

lintText(code: string, options?: FlatESLint.LintTextOptions): Promise<FlatESLint.LintResult[]>;

391

392

// Result processing

393

static outputFixes(results: FlatESLint.LintResult[]): Promise<void>;

394

static getErrorResults(results: FlatESLint.LintResult[]): FlatESLint.LintResult[];

395

396

// Configuration

397

calculateConfigForFile(filePath: string): Promise<FlatConfig.Config>;

398

isPathIgnored(filePath: string): Promise<boolean>;

399

400

// Formatter loading

401

loadFormatter(nameOrPath?: string): Promise<FlatESLint.Formatter>;

402

403

// Version information

404

static readonly version: string;

405

}

406

407

// ESLint options

408

namespace FlatESLint {

409

interface Options {

410

// Configuration

411

overrideConfigFile?: string | boolean;

412

overrideConfig?: FlatConfig.Config | FlatConfig.Config[];

413

414

// File handling

415

cwd?: string;

416

errorOnUnmatchedPattern?: boolean;

417

ignore?: boolean;

418

ignorePath?: string;

419

420

// Processing

421

fix?: boolean | ((message: Linter.LintMessage) => boolean);

422

fixTypes?: Array<Rule.RuleMetaData['type']>;

423

424

// Plugin handling

425

plugins?: Record<string, FlatConfig.Plugin>;

426

427

// Cache settings

428

cache?: boolean;

429

cacheLocation?: string;

430

cacheStrategy?: 'metadata' | 'content';

431

}

432

433

// Lint result

434

interface LintResult {

435

filePath: string;

436

messages: Linter.LintMessage[];

437

errorCount: number;

438

warningCount: number;

439

fixableErrorCount: number;

440

fixableWarningCount: number;

441

source?: string;

442

output?: string;

443

usedDeprecatedRules: DeprecatedRuleInfo[];

444

suppressedMessages: SuppressedMessage[];

445

}

446

447

// Text linting options

448

interface LintTextOptions {

449

filePath?: string;

450

warnIgnored?: boolean;

451

}

452

453

// Formatter interface

454

interface Formatter {

455

format(results: LintResult[], context?: FormatterContext): string | Promise<string>;

456

}

457

}

458

```

459

460

### Legacy ESLint Class

461

462

```typescript { .api }

463

class LegacyESLint {

464

constructor(options?: LegacyESLint.Options);

465

466

// Similar interface to FlatESLint but for classic config

467

lintFiles(patterns: string | string[]): Promise<LegacyESLint.LintResult[]>;

468

lintText(code: string, options?: LegacyESLint.LintTextOptions): Promise<LegacyESLint.LintResult[]>;

469

470

// Configuration methods

471

calculateConfigForFile(filePath: string): Promise<ClassicConfig.Config>;

472

isPathIgnored(filePath: string): Promise<boolean>;

473

474

// Static methods

475

static outputFixes(results: LegacyESLint.LintResult[]): Promise<void>;

476

static getErrorResults(results: LegacyESLint.LintResult[]): LegacyESLint.LintResult[];

477

}

478

```

479

480

### Linter Class

481

482

```typescript { .api }

483

class Linter {

484

constructor(options?: Linter.LinterOptions);

485

486

// Core linting

487

verifyAndFix(code: string, config: Linter.Config, options?: Linter.VerifyOptions): Linter.FixReport;

488

verify(code: string, config: Linter.Config, options?: Linter.VerifyOptions): Linter.LintMessage[];

489

490

// Source code operations

491

getSourceCode(filename?: string): SourceCode;

492

493

// Rule management

494

defineRule(ruleId: string, ruleModule: Rule.RuleModule): void;

495

defineRules(rules: Record<string, Rule.RuleModule>): void;

496

getRules(): Map<string, Rule.RuleModule>;

497

498

// Parser management

499

defineParser(parserId: string, parserModule: Parser.ParserModule): void;

500

501

// Configuration

502

static readonly version: string;

503

}

504

505

namespace Linter {

506

// Linter configuration

507

type Config = FlatConfig.Config;

508

509

// Linter options

510

interface LinterOptions {

511

cwd?: string;

512

}

513

514

// Verification options

515

interface VerifyOptions {

516

filename?: string;

517

allowInlineConfig?: boolean;

518

reportUnusedDisableDirectives?: boolean | 'error' | 'warn' | 'off';

519

disableFixes?: boolean;

520

}

521

522

// Lint message

523

interface LintMessage {

524

ruleId: string | null;

525

severity: Severity;

526

message: string;

527

line: number;

528

column: number;

529

endLine?: number;

530

endColumn?: number;

531

fix?: Rule.RuleFix;

532

suggestions?: Suggestion[];

533

nodeType: string;

534

messageId?: string;

535

}

536

537

// Message severity

538

type Severity = 1 | 2; // 1 = warning, 2 = error

539

540

// Fix report

541

interface FixReport {

542

fixed: boolean;

543

messages: LintMessage[];

544

output: string;

545

}

546

547

// Suggestion

548

interface Suggestion {

549

desc: string;

550

messageId?: string;

551

fix: Rule.RuleFix;

552

}

553

}

554

```

555

556

## Parser System

557

558

### Parser Namespace

559

560

```typescript { .api }

561

namespace Parser {

562

// Parser module interface

563

interface ParserModule {

564

parse(code: string, options?: ParserOptions): ParseResult;

565

parseForESLint?(code: string, options?: ParserOptions): ParseResult;

566

567

// Metadata

568

meta?: ParserMeta;

569

}

570

571

// Loose parser for configurations

572

interface LooseParserModule {

573

parse?: ParserModule['parse'];

574

parseForESLint?: ParserModule['parseForESLint'];

575

meta?: ParserMeta;

576

}

577

578

// Parse result

579

interface ParseResult {

580

ast: TSESTree.Program;

581

services?: ParserServices;

582

scopeManager?: Scope.ScopeManager;

583

visitorKeys?: VisitorKeys;

584

}

585

586

// Parser metadata

587

interface ParserMeta {

588

name?: string;

589

version?: string;

590

}

591

592

// Visitor keys for AST traversal

593

interface VisitorKeys {

594

[nodeType: string]: string[];

595

}

596

}

597

```

598

599

## Processor System

600

601

### Processor Namespace

602

603

```typescript { .api }

604

namespace Processor {

605

// Processor module interface

606

interface ProcessorModule {

607

preprocess?(text: string, filename: string): Array<string | TextInfo>;

608

postprocess?(messages: Linter.LintMessage[][], filename: string): Linter.LintMessage[];

609

610

// Metadata

611

meta?: ProcessorMeta;

612

613

// Support for supportsAutofix

614

supportsAutofix?: boolean;

615

}

616

617

// Loose processor for configurations

618

interface LooseProcessorModule {

619

preprocess?: ProcessorModule['preprocess'];

620

postprocess?: ProcessorModule['postprocess'];

621

meta?: ProcessorMeta;

622

supportsAutofix?: boolean;

623

}

624

625

// Processor metadata

626

interface ProcessorMeta {

627

name?: string;

628

version?: string;

629

}

630

631

// Text information for processing

632

interface TextInfo {

633

text: string;

634

filename: string;

635

}

636

637

// Pre-process function type

638

type PreProcess = NonNullable<ProcessorModule['preprocess']>;

639

640

// Post-process function type

641

type PostProcess = NonNullable<ProcessorModule['postprocess']>;

642

}

643

```

644

645

## Source Code

646

647

### SourceCode Class

648

649

```typescript { .api }

650

class SourceCode {

651

constructor(text: string, ast: TSESTree.Program);

652

constructor(config: SourceCode.Config);

653

654

// Text access

655

text: string;

656

ast: TSESTree.Program;

657

lines: string[];

658

hasBOM: boolean;

659

660

// Token access

661

getAllComments(): TSESTree.Comment[];

662

getComments(node: TSESTree.Node): { leading: TSESTree.Comment[]; trailing: TSESTree.Comment[] };

663

getCommentsAfter(nodeOrToken: TSESTree.Node | TSESTree.Token): TSESTree.Comment[];

664

getCommentsBefore(nodeOrToken: TSESTree.Node | TSESTree.Token): TSESTree.Comment[];

665

getCommentsInside(node: TSESTree.Node): TSESTree.Comment[];

666

667

// Token retrieval

668

getFirstToken(node: TSESTree.Node, options?: SourceCode.CursorWithSkipOptions): TSESTree.Token | null;

669

getFirstTokens(node: TSESTree.Node, options?: SourceCode.CursorWithCountOptions): TSESTree.Token[];

670

getLastToken(node: TSESTree.Node, options?: SourceCode.CursorWithSkipOptions): TSESTree.Token | null;

671

getLastTokens(node: TSESTree.Node, options?: SourceCode.CursorWithCountOptions): TSESTree.Token[];

672

673

getTokenAfter(nodeOrToken: TSESTree.Node | TSESTree.Token, options?: SourceCode.CursorWithSkipOptions): TSESTree.Token | null;

674

getTokenBefore(nodeOrToken: TSESTree.Node | TSESTree.Token, options?: SourceCode.CursorWithSkipOptions): TSESTree.Token | null;

675

getTokensAfter(nodeOrToken: TSESTree.Node | TSESTree.Token, options?: SourceCode.CursorWithCountOptions): TSESTree.Token[];

676

getTokensBefore(nodeOrToken: TSESTree.Node | TSESTree.Token, options?: SourceCode.CursorWithCountOptions): TSESTree.Token[];

677

678

getTokens(node: TSESTree.Node, beforeCount?: number, afterCount?: number): TSESTree.Token[];

679

getTokensByRangeStart(range: TSESTree.Range, options?: SourceCode.FilterPredicate): TSESTree.Token[];

680

681

// Text operations

682

getText(node?: TSESTree.Node | TSESTree.Token, beforeCount?: number, afterCount?: number): string;

683

getLines(): string[];

684

685

// Location utilities

686

getLocFromIndex(index: number): TSESTree.LineAndColumnData;

687

getIndexFromLoc(location: TSESTree.LineAndColumnData): number;

688

}

689

690

namespace SourceCode {

691

// Configuration interface

692

interface Config {

693

text: string;

694

ast: TSESTree.Program;

695

parserServices?: ParserServices;

696

scopeManager?: Scope.ScopeManager;

697

visitorKeys?: Parser.VisitorKeys;

698

}

699

700

// Token filtering options

701

interface CursorWithSkipOptions {

702

skip?: number;

703

includeComments?: boolean;

704

filter?: FilterPredicate;

705

}

706

707

interface CursorWithCountOptions {

708

count?: number;

709

includeComments?: boolean;

710

filter?: FilterPredicate;

711

}

712

713

// Filter predicate

714

type FilterPredicate = (tokenOrComment: TSESTree.Token | TSESTree.Comment) => boolean;

715

}

716

```

717

718

## Scope Analysis

719

720

### Scope Namespace

721

722

```typescript { .api }

723

namespace Scope {

724

// Main scope manager

725

interface ScopeManager {

726

scopes: Scope[];

727

globalScope: GlobalScope | null;

728

acquire(node: TSESTree.Node, inner?: boolean): Scope | null;

729

getDeclaredVariables(node: TSESTree.Node): Variable[];

730

}

731

732

// Base scope interface

733

interface Scope {

734

type: ScopeType;

735

isStrict: boolean;

736

upper: Scope | null;

737

childScopes: Scope[];

738

variableScope: Scope;

739

block: TSESTree.Node;

740

variables: Variable[];

741

references: Reference[];

742

743

// Scope methods

744

set: Map<string, Variable>;

745

through: Reference[];

746

}

747

748

// Scope types

749

type ScopeType =

750

| 'block'

751

| 'catch'

752

| 'class'

753

| 'for'

754

| 'function'

755

| 'function-expression-name'

756

| 'global'

757

| 'module'

758

| 'switch'

759

| 'with'

760

| 'TSDeclareFunction'

761

| 'TSModuleBlock';

762

763

// Global scope

764

interface GlobalScope extends Scope {

765

type: 'global';

766

childScopes: Scope[];

767

through: Reference[];

768

}

769

770

// Variable definition

771

interface Variable {

772

name: string;

773

identifiers: TSESTree.Identifier[];

774

references: Reference[];

775

defs: Definition[];

776

777

// Variable properties

778

scope: Scope;

779

eslintExplicitGlobal?: boolean;

780

eslintExplicitGlobalComments?: TSESTree.Comment[];

781

eslintImplicitGlobalSetting?: 'readonly' | 'writable';

782

writeable?: boolean;

783

}

784

785

// Variable reference

786

interface Reference {

787

identifier: TSESTree.Identifier;

788

from: Scope;

789

resolved: Variable | null;

790

writeExpr: TSESTree.Node | null;

791

init: boolean;

792

793

// Reference flags

794

isWrite(): boolean;

795

isRead(): boolean;

796

isWriteOnly(): boolean;

797

isReadOnly(): boolean;

798

isReadWrite(): boolean;

799

}

800

801

// Variable definition

802

interface Definition {

803

type: DefinitionType;

804

name: TSESTree.Identifier;

805

node: TSESTree.Node;

806

parent?: TSESTree.Node;

807

index?: number;

808

kind?: string;

809

}

810

811

// Definition types

812

type DefinitionType =

813

| 'CatchClause'

814

| 'ClassName'

815

| 'FunctionName'

816

| 'ImplicitGlobalVariable'

817

| 'ImportBinding'

818

| 'Parameter'

819

| 'TSEnumName'

820

| 'TSEnumMemberName'

821

| 'TSModuleName'

822

| 'Variable';

823

}

824

```

825

826

## Complete Rule Example

827

828

### Type-Safe Rule Implementation

829

830

```typescript { .api }

831

import { ESLintUtils, TSESLint, TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils';

832

833

type Options = [{

834

allowShortCircuit?: boolean;

835

allowTernary?: boolean;

836

allowTaggedTemplates?: boolean;

837

}];

838

839

type MessageIds = 'unusedExpression' | 'unusedValue';

840

841

const createRule = ESLintUtils.RuleCreator(name => `https://example.com/${name}`);

842

843

export default createRule<Options, MessageIds>({

844

name: 'no-unused-expressions',

845

meta: {

846

type: 'suggestion',

847

docs: {

848

description: 'Disallow unused expressions',

849

recommended: false

850

},

851

messages: {

852

unusedExpression: 'Expected an assignment or function call and instead saw an expression.',

853

unusedValue: 'Expected an assignment or function call and instead saw an unused value.'

854

},

855

schema: [{

856

type: 'object',

857

properties: {

858

allowShortCircuit: { type: 'boolean' },

859

allowTernary: { type: 'boolean' },

860

allowTaggedTemplates: { type: 'boolean' }

861

},

862

additionalProperties: false

863

}]

864

},

865

defaultOptions: [{

866

allowShortCircuit: false,

867

allowTernary: false,

868

allowTaggedTemplates: false

869

}],

870

create(context: TSESLint.RuleContext<MessageIds, Options>, [options]) {

871

const sourceCode = context.sourceCode;

872

873

function isDirectiveComment(node: TSESTree.Node): boolean {

874

const comments = sourceCode.getCommentsBefore(node);

875

return comments.some(comment =>

876

comment.type === 'Line' && comment.value.trim().startsWith('eslint-')

877

);

878

}

879

880

function checkExpressionStatement(node: TSESTree.ExpressionStatement): void {

881

if (isDirectiveComment(node)) {

882

return;

883

}

884

885

const { expression } = node;

886

let messageId: MessageIds = 'unusedExpression';

887

888

// Check for allowed patterns

889

if (options.allowShortCircuit) {

890

if (expression.type === AST_NODE_TYPES.LogicalExpression &&

891

(expression.operator === '&&' || expression.operator === '||')) {

892

return;

893

}

894

}

895

896

if (options.allowTernary && expression.type === AST_NODE_TYPES.ConditionalExpression) {

897

return;

898

}

899

900

if (options.allowTaggedTemplates && expression.type === AST_NODE_TYPES.TaggedTemplateExpression) {

901

return;

902

}

903

904

// Determine appropriate message

905

if (expression.type === AST_NODE_TYPES.AssignmentExpression ||

906

expression.type === AST_NODE_TYPES.CallExpression ||

907

expression.type === AST_NODE_TYPES.NewExpression) {

908

return; // These are generally allowed

909

}

910

911

if (expression.type === AST_NODE_TYPES.UnaryExpression ||

912

expression.type === AST_NODE_TYPES.BinaryExpression) {

913

messageId = 'unusedValue';

914

}

915

916

context.report({

917

node: expression,

918

messageId

919

});

920

}

921

922

const ruleListener: TSESLint.RuleListener = {

923

ExpressionStatement: checkExpressionStatement,

924

925

Program() {

926

// Program-level setup

927

const scope = context.sourceCode.scopeManager?.acquire(context.sourceCode.ast, false);

928

if (scope) {

929

// Analyze global scope if needed

930

}

931

}

932

};

933

934

return ruleListener;

935

}

936

});

937

```

938

939

### Rule Testing Interface

940

941

```typescript { .api }

942

// Rule tester (deprecated - use @typescript-eslint/rule-tester instead)

943

class RuleTester {

944

constructor(config?: RuleTester.Config);

945

946

run<MessageIds extends string, Options extends readonly unknown[]>(

947

name: string,

948

rule: TSESLint.RuleModule<MessageIds, Options>,

949

tests: {

950

valid: RuleTester.ValidTestCase<Options>[];

951

invalid: RuleTester.InvalidTestCase<MessageIds, Options>[];

952

}

953

): void;

954

}

955

956

namespace RuleTester {

957

interface Config {

958

parser?: string;

959

parserOptions?: ParserOptions;

960

globals?: Record<string, boolean>;

961

settings?: Record<string, unknown>;

962

}

963

964

interface ValidTestCase<Options extends readonly unknown[]> {

965

code: string;

966

options?: Options;

967

filename?: string;

968

parserOptions?: ParserOptions;

969

settings?: Record<string, unknown>;

970

globals?: Record<string, boolean>;

971

}

972

973

interface InvalidTestCase<MessageIds extends string, Options extends readonly unknown[]>

974

extends ValidTestCase<Options> {

975

errors: TestCaseError<MessageIds>[];

976

output?: string | null;

977

}

978

979

interface TestCaseError<MessageIds extends string> {

980

messageId: MessageIds;

981

data?: Record<string, unknown>;

982

type?: string;

983

line?: number;

984

column?: number;

985

endLine?: number;

986

endColumn?: number;

987

suggestions?: SuggestionOutput<MessageIds>[];

988

}

989

990

interface SuggestionOutput<MessageIds extends string> {

991

messageId: MessageIds;

992

output: string;

993

data?: Record<string, unknown>;

994

}

995

}

996

```