or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdconfiguration.mdformatters.mdindex.mdlinting.mdrules.mdtesting.md

rules.mddocs/

0

# Rules

1

2

TSLint includes 163+ built-in rules organized by category and provides comprehensive APIs for developing custom rules.

3

4

## Built-in Rules

5

6

### Rule Categories

7

8

TSLint rules are organized into five categories:

9

10

1. **Functionality** (65 rules) - Correctness and best practices

11

2. **Maintainability** (14 rules) - Code maintainability

12

3. **Style** (79 rules) - Coding style and conventions

13

4. **TypeScript** (rules in other categories marked as TypeScript-specific)

14

5. **Formatting** (5 rules) - Code formatting

15

16

### Functionality Rules

17

18

These rules catch common errors and enforce best practices:

19

20

#### Core Functionality Rules

21

22

- `await-promise` - Requires that the results of async functions be awaited

23

- `ban-comma-operator` - Disallows the comma operator

24

- `ban-ts-ignore` - Bans `@ts-ignore` comments from being used

25

- `ban` - Bans the use of specific functions or global methods

26

- `curly` - Enforces braces for if/for/do/while statements

27

- `forin` - Requires a `for ... in` statement to be filtered with an `if` statement

28

- `function-constructor` - Prevents using the built-in Function constructor

29

- `import-blacklist` - Disallows importing the specified modules

30

- `label-position` - Only allows labels in sensible locations

31

- `no-arg` - Disallows use of `arguments.caller` and `arguments.callee`

32

33

#### Variable and Scope Rules

34

35

- `no-duplicate-variable` - Disallows duplicate variable declarations

36

- `no-shadowed-variable` - Disallows shadowing variable declarations

37

- `no-unused-expression` - Disallows unused expression statements

38

- `no-unused-variable` - Disallows unused imports, variables, functions and private members

39

- `no-use-before-declare` - Disallows usage of variables before their declaration

40

- `no-var-keyword` - Disallows usage of the `var` keyword

41

- `no-var-requires` - Disallows the use of require statements except in import statements

42

43

#### Type Safety Rules

44

45

- `no-unsafe-any` - Warns when using an expression of type 'any' in unsafe way

46

- `no-unsafe-finally` - Disallows control flow statements in finally blocks

47

- `restrict-plus-operands` - Requires both operands of addition to be the same type

48

- `strict-boolean-expressions` - Restricts the types allowed in boolean expressions

49

- `strict-type-predicates` - Warns for type predicates that are always true or always false

50

- `use-isnan` - Enforces use of the `isNaN()` function

51

52

### Maintainability Rules

53

54

Rules focused on code maintainability and complexity:

55

56

- `cyclomatic-complexity` - Enforces a threshold of cyclomatic complexity

57

- `deprecation` - Warns when deprecated APIs are used

58

- `max-classes-per-file` - Maximum number of classes per file

59

- `max-file-line-count` - Requires files to remain under a certain number of lines

60

- `max-line-length` - Requires lines to be under a certain max length

61

- `no-default-export` - Disallows default exports in ES6-style modules

62

- `no-default-import` - Avoid import statements with side-effect imports

63

- `no-duplicate-imports` - Disallows multiple import statements from the same module

64

- `no-mergeable-namespace` - Disallows mergeable namespaces in the same file

65

- `no-require-imports` - Disallows invocation of `require()`

66

- `object-literal-sort-keys` - Checks ordering of keys in object literals

67

- `prefer-const` - Requires that variable declarations use `const` instead of `let` if possible

68

- `prefer-readonly` - Requires that private variables are marked as `readonly` if they're never modified outside of the constructor

69

- `trailing-comma` - Requires or disallows trailing commas in array and object literals, destructuring assignments, function typings, named imports and exports and function parameters

70

71

### Style Rules

72

73

Rules for consistent coding style:

74

75

#### Naming and Casing

76

77

- `class-name` - Enforces PascalCase for classes and interfaces

78

- `file-name-casing` - Enforces consistent file naming conventions

79

- `interface-name` - Requires interface names to begin with a capital 'I'

80

- `match-default-export-name` - Requires that a default import have the same name as the declaration it imports

81

- `variable-name` - Checks variable names for various errors

82

83

#### Comments and Documentation

84

85

- `comment-format` - Enforces formatting rules for single-line comments

86

- `comment-type` - Requires or forbids JSDoc style comments for classes, interfaces, functions, etc.

87

- `completed-docs` - Enforces documentation for important items be filled out

88

- `file-header` - Enforces a certain header comment for all files, matched by a regular expression

89

- `jsdoc-format` - Enforces basic format rules for JSDoc comments

90

- `no-redundant-jsdoc` - Forbids JSDoc which duplicates TypeScript functionality

91

92

#### Import and Module Style

93

94

- `import-spacing` - Ensures proper spacing between import statement elements

95

- `no-import-side-effect` - Avoid import statements with side-effect imports

96

- `no-reference` - Disallows `/// <reference>` imports

97

- `no-reference-import` - Don't `<reference types="foo" />` if you import `foo` anyway

98

- `ordered-imports` - Requires that import statements be alphabetized and grouped

99

100

### Formatting Rules

101

102

Rules for consistent code formatting:

103

104

- `eofline` - Ensures the file ends with a newline

105

- `indent` - Enforces indentation with tabs or spaces

106

- `linebreak-style` - Enforces consistent linebreak style

107

- `semicolon` - Enforces consistent semicolon usage

108

- `whitespace` - Enforces whitespace style conventions

109

110

## Custom Rule Development

111

112

TSLint provides comprehensive APIs for developing custom rules.

113

114

### Rule Base Classes

115

116

```typescript { .api }

117

import { Rules } from 'tslint';

118

119

// Base rule class

120

abstract class AbstractRule implements IRule {

121

static metadata: IRuleMetadata;

122

123

constructor(options: IOptions)

124

getOptions(): IOptions

125

isEnabled(): boolean

126

apply(sourceFile: ts.SourceFile): RuleFailure[]

127

applyWithWalker(walker: IWalker): RuleFailure[]

128

129

// Helper method for simple rules

130

applyWithFunction(

131

sourceFile: ts.SourceFile,

132

walkFn: (ctx: WalkContext<T>) => void,

133

options?: T

134

): RuleFailure[]

135

}

136

137

// Type-aware rule base class

138

abstract class TypedRule extends AbstractRule implements ITypedRule {

139

applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[]

140

}

141

142

// Optional type-aware rule base class

143

abstract class OptionallyTypedRule extends AbstractRule {

144

apply(sourceFile: ts.SourceFile): RuleFailure[]

145

applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[]

146

}

147

```

148

149

### Rule Metadata

150

151

```typescript { .api }

152

interface IRuleMetadata {

153

ruleName: string;

154

type: "functionality" | "maintainability" | "style" | "typescript" | "formatting";

155

deprecationMessage?: string;

156

description: string;

157

descriptionDetails?: string;

158

hasFix?: boolean;

159

optionsDescription: string;

160

options: any;

161

optionExamples?: Array<true | any[]> | string[] | Array<{ options: any }>;

162

rationale?: string;

163

requiresTypeInfo?: boolean;

164

typescriptOnly: boolean;

165

codeExamples?: ICodeExample[];

166

}

167

168

interface ICodeExample {

169

description: string;

170

config: string;

171

pass?: string;

172

fail?: string;

173

}

174

```

175

176

### Rule Options Interface

177

178

```typescript { .api }

179

interface IOptions {

180

ruleArguments: any[];

181

ruleSeverity: "warning" | "error" | "off";

182

ruleName: string;

183

disabledIntervals: IDisabledInterval[];

184

}

185

```

186

187

### Rule Failure System

188

189

```typescript { .api }

190

class RuleFailure {

191

constructor(

192

sourceFile: ts.SourceFile,

193

start: number,

194

end: number,

195

failure: string,

196

ruleName: string,

197

fix?: Fix

198

)

199

200

// Position methods

201

getFileName(): string

202

getStartPosition(): RuleFailurePosition

203

getEndPosition(): RuleFailurePosition

204

205

// Content methods

206

getFailure(): string

207

getRuleName(): string

208

getRuleSeverity(): RuleSeverity

209

setRuleSeverity(value: RuleSeverity): void

210

211

// Fix methods

212

hasFix(): boolean

213

getFix(): Fix | undefined

214

215

// Serialization

216

toJson(): IRuleFailureJson

217

equals(other: RuleFailure): boolean

218

static compare(a: RuleFailure, b: RuleFailure): number

219

}

220

221

interface RuleFailurePosition {

222

character: number;

223

line: number;

224

position: number;

225

}

226

227

interface IRuleFailureJson {

228

endPosition: IRuleFailurePositionJson;

229

failure: string;

230

fix?: FixJson;

231

name: string;

232

ruleSeverity: string;

233

ruleName: string;

234

startPosition: IRuleFailurePositionJson;

235

}

236

237

interface IRuleFailurePositionJson {

238

character: number;

239

line: number;

240

position: number;

241

}

242

243

interface ReplacementJson {

244

innerStart: number;

245

innerLength: number;

246

innerText: string;

247

}

248

249

type FixJson = ReplacementJson | ReplacementJson[];

250

type RuleSeverity = "warning" | "error" | "off";

251

type Fix = Replacement | Replacement[];

252

```

253

254

### Replacement System

255

256

```typescript { .api }

257

class Replacement {

258

constructor(start: number, length: number, text: string)

259

260

// Static factory methods

261

static replaceNode(node: ts.Node, text: string, sourceFile?: ts.SourceFile): Replacement

262

static replaceFromTo(start: number, end: number, text: string): Replacement

263

static deleteText(start: number, length: number): Replacement

264

static deleteFromTo(start: number, end: number): Replacement

265

static appendText(start: number, text: string): Replacement

266

267

// Application methods

268

apply(content: string): string

269

static applyAll(content: string, replacements: Replacement[]): string

270

static applyFixes(content: string, fixes: Fix[]): string

271

}

272

```

273

274

## Custom Rule Examples

275

276

### Simple Rule Example

277

278

```typescript

279

import { Rules, RuleFailure, RuleWalker } from 'tslint';

280

import * as ts from 'typescript';

281

282

export class Rule extends Rules.AbstractRule {

283

public static metadata: Rules.IRuleMetadata = {

284

ruleName: 'no-console-log',

285

description: 'Disallows console.log() calls',

286

optionsDescription: 'Not configurable.',

287

options: null,

288

optionExamples: [true],

289

type: 'functionality',

290

typescriptOnly: false,

291

};

292

293

public apply(sourceFile: ts.SourceFile): RuleFailure[] {

294

return this.applyWithFunction(sourceFile, walk);

295

}

296

}

297

298

function walk(ctx: Rules.WalkContext<void>) {

299

function cb(node: ts.Node): void {

300

if (ts.isCallExpression(node) &&

301

ts.isPropertyAccessExpression(node.expression) &&

302

node.expression.expression.getText() === 'console' &&

303

node.expression.name.text === 'log') {

304

305

ctx.addFailureAtNode(node, 'console.log() is not allowed');

306

}

307

return ts.forEachChild(node, cb);

308

}

309

return ts.forEachChild(ctx.sourceFile, cb);

310

}

311

```

312

313

### Rule with Options

314

315

```typescript

316

import { Rules, RuleFailure } from 'tslint';

317

import * as ts from 'typescript';

318

319

interface Options {

320

maxLength: number;

321

}

322

323

export class Rule extends Rules.AbstractRule {

324

public static metadata: Rules.IRuleMetadata = {

325

ruleName: 'max-identifier-length',

326

description: 'Enforces maximum identifier length',

327

optionsDescription: 'Maximum identifier length (default: 50)',

328

options: {

329

type: 'object',

330

properties: {

331

maxLength: { type: 'number' }

332

}

333

},

334

optionExamples: [

335

[true, { maxLength: 30 }]

336

],

337

type: 'style',

338

typescriptOnly: false,

339

};

340

341

public apply(sourceFile: ts.SourceFile): RuleFailure[] {

342

const options: Options = {

343

maxLength: 50,

344

...this.ruleArguments[0]

345

};

346

347

return this.applyWithFunction(sourceFile, walk, options);

348

}

349

}

350

351

function walk(ctx: Rules.WalkContext<Options>) {

352

function cb(node: ts.Node): void {

353

if (ts.isIdentifier(node) &&

354

node.text.length > ctx.options.maxLength) {

355

356

ctx.addFailureAtNode(

357

node,

358

`Identifier '${node.text}' exceeds maximum length of ${ctx.options.maxLength}`

359

);

360

}

361

return ts.forEachChild(node, cb);

362

}

363

return ts.forEachChild(ctx.sourceFile, cb);

364

}

365

```

366

367

### Type-Aware Rule

368

369

```typescript

370

import { Rules, RuleFailure } from 'tslint';

371

import * as ts from 'typescript';

372

373

export class Rule extends Rules.TypedRule {

374

public static metadata: Rules.IRuleMetadata = {

375

ruleName: 'no-unused-promise',

376

description: 'Disallows unused Promise return values',

377

optionsDescription: 'Not configurable.',

378

options: null,

379

optionExamples: [true],

380

type: 'functionality',

381

typescriptOnly: false,

382

requiresTypeInfo: true,

383

};

384

385

public applyWithProgram(

386

sourceFile: ts.SourceFile,

387

program: ts.Program

388

): RuleFailure[] {

389

return this.applyWithFunction(

390

sourceFile,

391

walk,

392

undefined,

393

program.getTypeChecker()

394

);

395

}

396

}

397

398

function walk(ctx: Rules.WalkContext<void>, tc: ts.TypeChecker) {

399

function cb(node: ts.Node): void {

400

if (ts.isExpressionStatement(node) &&

401

ts.isCallExpression(node.expression)) {

402

403

const type = tc.getTypeAtLocation(node.expression);

404

const typeString = tc.typeToString(type);

405

406

if (typeString.includes('Promise<')) {

407

ctx.addFailureAtNode(

408

node.expression,

409

'Promise return value is unused'

410

);

411

}

412

}

413

return ts.forEachChild(node, cb);

414

}

415

return ts.forEachChild(ctx.sourceFile, cb);

416

}

417

```

418

419

### Rule with Auto-fix

420

421

```typescript

422

import { Rules, RuleFailure, Replacement } from 'tslint';

423

import * as ts from 'typescript';

424

425

export class Rule extends Rules.AbstractRule {

426

public static metadata: Rules.IRuleMetadata = {

427

ruleName: 'prefer-const-assertion',

428

description: 'Prefer const assertion over type annotation',

429

optionsDescription: 'Not configurable.',

430

options: null,

431

optionExamples: [true],

432

type: 'style',

433

typescriptOnly: true,

434

hasFix: true,

435

};

436

437

public apply(sourceFile: ts.SourceFile): RuleFailure[] {

438

return this.applyWithFunction(sourceFile, walk);

439

}

440

}

441

442

function walk(ctx: Rules.WalkContext<void>) {

443

function cb(node: ts.Node): void {

444

if (ts.isVariableDeclaration(node) &&

445

node.type &&

446

node.initializer &&

447

ts.isAsExpression(node.initializer)) {

448

449

const fix = [

450

Replacement.deleteFromTo(node.type.pos, node.type.end),

451

Replacement.replaceNode(

452

node.initializer,

453

`${node.initializer.expression.getText()} as const`

454

)

455

];

456

457

ctx.addFailureAtNode(

458

node,

459

'Prefer const assertion over type annotation',

460

fix

461

);

462

}

463

return ts.forEachChild(node, cb);

464

}

465

return ts.forEachChild(ctx.sourceFile, cb);

466

}

467

```

468

469

## Walker System (Legacy)

470

471

TSLint includes a walker system for AST traversal, though `applyWithFunction` is now preferred.

472

473

### Walker Classes

474

475

```typescript { .api }

476

// Basic walker interface

477

interface IWalker {

478

getSourceFile(): ts.SourceFile;

479

walk(sourceFile: ts.SourceFile): void;

480

getFailures(): RuleFailure[];

481

}

482

483

// Abstract walker base class

484

abstract class AbstractWalker<T = undefined> extends WalkContext<T> implements IWalker {

485

abstract walk(sourceFile: ts.SourceFile): void;

486

getSourceFile(): ts.SourceFile;

487

getFailures(): RuleFailure[];

488

}

489

490

// Base syntax walker class

491

class SyntaxWalker {

492

walk(node: ts.Node): void;

493

protected visitNode(node: ts.Node): void;

494

protected walkChildren(node: ts.Node): void;

495

}

496

497

// Legacy rule walker (deprecated)

498

class RuleWalker extends SyntaxWalker {

499

// Prefer applyWithFunction over extending RuleWalker

500

}

501

```

502

503

### Walk Context

504

505

```typescript { .api }

506

class WalkContext<T = undefined> {

507

constructor(sourceFile: ts.SourceFile, ruleName: string, options?: T)

508

509

// Failure reporting

510

addFailure(start: number, end: number, message: string, fix?: Fix): void

511

addFailureAt(position: number, width: number, message: string, fix?: Fix): void

512

addFailureAtNode(node: ts.Node, message: string, fix?: Fix): void

513

514

// Properties

515

readonly sourceFile: ts.SourceFile

516

readonly failures: RuleFailure[]

517

readonly options: T | undefined

518

readonly ruleName: string

519

}

520

```

521

522

## Rule Loading and Registration

523

524

### Custom Rules Directory

525

526

```typescript

527

// In tslint.json

528

{

529

"rulesDirectory": [

530

"./custom-rules",

531

"node_modules/custom-tslint-rules/lib"

532

],

533

"rules": {

534

"my-custom-rule": true

535

}

536

}

537

```

538

539

### Rule Naming Convention

540

541

Rules should be exported with PascalCase class names and kebab-case file names:

542

543

- File: `no-console-log.ts` or `no-console-log.js`

544

- Class: `export class Rule extends Rules.AbstractRule`

545

546

### Rule Distribution

547

548

```typescript

549

// Package custom rules as npm module

550

// package.json

551

{

552

"name": "my-tslint-rules",

553

"main": "lib/index.js",

554

"files": ["lib/"]

555

}

556

557

// lib/noConsoleLogRule.ts

558

export class Rule extends Rules.AbstractRule {

559

// Rule implementation

560

}

561

```

562

563

## Best Practices

564

565

### Rule Development Guidelines

566

567

1. **Use `applyWithFunction`** instead of extending RuleWalker

568

2. **Provide comprehensive metadata** including examples and rationale

569

3. **Include auto-fixes** where appropriate using the Replacement API

570

4. **Handle edge cases** and provide clear error messages

571

5. **Test thoroughly** using TSLint's testing framework

572

6. **Follow naming conventions** for rule files and classes

573

7. **Document rule behavior** with clear descriptions and examples

574

575

### Performance Considerations

576

577

1. **Minimize AST traversal** - Use targeted node visitors

578

2. **Cache expensive computations** - Reuse TypeScript type checker results

579

3. **Avoid deep recursion** - Use iterative approaches where possible

580

4. **Limit rule scope** - Don't check unnecessary node types

581

582

### Error Handling

583

584

```typescript

585

function walk(ctx: Rules.WalkContext<void>) {

586

function cb(node: ts.Node): void {

587

try {

588

// Rule logic here

589

if (someCondition(node)) {

590

ctx.addFailureAtNode(node, 'Rule violation');

591

}

592

} catch (error) {

593

// Log error but don't fail linting

594

console.warn(`Rule error: ${error.message}`);

595

}

596

return ts.forEachChild(node, cb);

597

}

598

return ts.forEachChild(ctx.sourceFile, cb);

599

}

600

```