or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

best-practices-rules.mdcode-quality-rules.mdindex.mdplugin-configuration.mdstyle-formatting-rules.mdtype-safety-rules.md

best-practices-rules.mddocs/

0

# Best Practices Rules

1

2

Rules that promote TypeScript and JavaScript best practices, preventing common pitfalls and anti-patterns. These rules help developers write more maintainable, performant, and error-resistant code.

3

4

## Capabilities

5

6

### Modern Language Features

7

8

Rules that encourage the use of modern JavaScript and TypeScript features.

9

10

```typescript { .api }

11

/**

12

* Prefer optional chaining over chained logical ands

13

*/

14

"prefer-optional-chain": RuleModule;

15

16

/**

17

* Enforce nullish coalescing over logical OR

18

*/

19

"prefer-nullish-coalescing": RuleModule;

20

21

/**

22

* Prefer as const over literal type assertions

23

*/

24

"prefer-as-const": RuleModule;

25

26

/**

27

* Prefer for-of loop over traditional for loop when possible

28

*/

29

"prefer-for-of": RuleModule;

30

31

/**

32

* Prefer includes method over indexOf method

33

*/

34

"prefer-includes": RuleModule;

35

36

/**

37

* Prefer string startsWith/endsWith over substring/indexOf

38

*/

39

"prefer-string-starts-ends-with": RuleModule;

40

41

/**

42

* Prefer destructuring from arrays and objects

43

*/

44

"prefer-destructuring": RuleModule;

45

```

46

47

**Usage Examples:**

48

49

```typescript

50

// ❌ Bad - prefer-optional-chain

51

if (user && user.profile && user.profile.settings) {

52

// Access user.profile.settings

53

}

54

55

// ✅ Good

56

if (user?.profile?.settings) {

57

// Access user.profile.settings

58

}

59

60

// ❌ Bad - prefer-nullish-coalescing

61

const name = user.name || 'Anonymous'; // Wrong for empty string

62

63

// ✅ Good

64

const name = user.name ?? 'Anonymous'; // Only null/undefined

65

66

// ❌ Bad - prefer-includes

67

if (items.indexOf(item) !== -1) {} // Verbose

68

69

// ✅ Good

70

if (items.includes(item)) {} // Clear intent

71

72

// ❌ Bad - prefer-string-starts-ends-with

73

if (str.substring(0, 6) === 'prefix') {} // Verbose

74

75

// ✅ Good

76

if (str.startsWith('prefix')) {} // Clear intent

77

```

78

79

### Type Safety Best Practices

80

81

Rules that promote safer TypeScript patterns and discourage risky operations.

82

83

```typescript { .api }

84

/**

85

* Prefer using type parameter when calling Array#reduce

86

*/

87

"prefer-reduce-type-parameter": RuleModule;

88

89

/**

90

* Prefer return this type for methods returning this

91

*/

92

"prefer-return-this-type": RuleModule;

93

94

/**

95

* Prefer using the built-in comparison function in Array.sort()

96

*/

97

"require-array-sort-compare": RuleModule;

98

99

/**

100

* Require that function parameters are used

101

*/

102

"no-unused-vars": RuleModule;

103

104

/**

105

* Disallow unnecessary parameter property assignment

106

*/

107

"no-unnecessary-parameter-property-assignment": RuleModule;

108

109

/**

110

* Prefer readonly arrays and tuples

111

*/

112

"prefer-readonly": RuleModule;

113

114

/**

115

* Prefer readonly parameter types in function declarations

116

*/

117

"prefer-readonly-parameter-types": RuleModule;

118

```

119

120

**Usage Examples:**

121

122

```typescript

123

// ❌ Bad - prefer-reduce-type-parameter

124

const result = items.reduce((acc, item) => acc + item.value, 0);

125

126

// ✅ Good

127

const result = items.reduce<number>((acc, item) => acc + item.value, 0);

128

129

// ❌ Bad - require-array-sort-compare

130

numbers.sort(); // Lexicographic sort, not numeric

131

132

// ✅ Good

133

numbers.sort((a, b) => a - b); // Proper numeric sort

134

135

// ❌ Bad - prefer-readonly

136

function processItems(items: Item[]) {} // Mutable array

137

138

// ✅ Good

139

function processItems(items: readonly Item[]) {} // Immutable array

140

```

141

142

### Error Handling Best Practices

143

144

Rules that promote proper error handling and exception management.

145

146

```typescript { .api }

147

/**

148

* Require using Error objects as Promise rejection reasons

149

*/

150

"prefer-promise-reject-errors": RuleModule;

151

152

/**

153

* Enforce "throw" expressions to only throw Error objects

154

*/

155

"only-throw-error": RuleModule;

156

157

/**

158

* Require catch clauses to use unknown type for error

159

*/

160

"use-unknown-in-catch-callback-variable": RuleModule;

161

162

/**

163

* Disallow the use of eval()-like functions

164

*/

165

"no-implied-eval": RuleModule;

166

167

/**

168

* Disallow returning await in try/catch/finally

169

*/

170

"return-await": RuleModule;

171

172

/**

173

* Require async functions to have await

174

*/

175

"require-await": RuleModule;

176

```

177

178

**Usage Examples:**

179

180

```typescript

181

// ❌ Bad - only-throw-error

182

throw 'Something went wrong'; // String instead of Error

183

184

// ✅ Good

185

throw new Error('Something went wrong');

186

187

// ❌ Bad - use-unknown-in-catch-callback-variable

188

try {

189

riskyOperation();

190

} catch (error) { // error is implicitly any

191

console.log(error.message);

192

}

193

194

// ✅ Good

195

try {

196

riskyOperation();

197

} catch (error: unknown) {

198

if (error instanceof Error) {

199

console.log(error.message);

200

}

201

}

202

203

// ❌ Bad - prefer-promise-reject-errors

204

return Promise.reject('Failed'); // String rejection

205

206

// ✅ Good

207

return Promise.reject(new Error('Failed')); // Error object

208

```

209

210

### Function and Method Best Practices

211

212

Rules that promote better function and method design patterns.

213

214

```typescript { .api }

215

/**

216

* Require promise-returning functions to be async

217

*/

218

"promise-function-async": RuleModule;

219

220

/**

221

* Disallow functions that don't use this

222

*/

223

"class-methods-use-this": RuleModule;

224

225

/**

226

* Require consistent return statements

227

*/

228

"consistent-return": RuleModule;

229

230

/**

231

* Disallow unbound methods

232

*/

233

"unbound-method": RuleModule;

234

235

/**

236

* Require default parameters to be last

237

*/

238

"default-param-last": RuleModule;

239

240

/**

241

* Enforce maximum number of parameters

242

*/

243

"max-params": RuleModule;

244

```

245

246

**Usage Examples:**

247

248

```typescript

249

// ❌ Bad - promise-function-async

250

function fetchData() { // Should be async

251

return fetch('/api/data');

252

}

253

254

// ✅ Good

255

async function fetchData() {

256

return fetch('/api/data');

257

}

258

259

// ❌ Bad - unbound-method

260

const obj = { method() { return this.value; } };

261

const fn = obj.method; // Lost context

262

263

// ✅ Good

264

const obj = { method() { return this.value; } };

265

const fn = obj.method.bind(obj); // Bound context

266

```

267

268

### Loop and Iteration Best Practices

269

270

Rules that promote efficient and safe iteration patterns.

271

272

```typescript { .api }

273

/**

274

* Prefer for-of loops over traditional for loops

275

*/

276

"prefer-for-of": RuleModule;

277

278

/**

279

* Disallow for-in loops over arrays

280

*/

281

"no-for-in-array": RuleModule;

282

283

/**

284

* Prefer Array.find() over filter()[0]

285

*/

286

"prefer-find": RuleModule;

287

288

/**

289

* Disallow function declarations in nested blocks

290

*/

291

"no-loop-func": RuleModule;

292

```

293

294

**Usage Examples:**

295

296

```typescript

297

// ❌ Bad - prefer-for-of

298

for (let i = 0; i < items.length; i++) {

299

console.log(items[i]);

300

}

301

302

// ✅ Good

303

for (const item of items) {

304

console.log(item);

305

}

306

307

// ❌ Bad - no-for-in-array

308

for (const index in array) { // for-in on array

309

console.log(array[index]);

310

}

311

312

// ✅ Good

313

for (const item of array) {

314

console.log(item);

315

}

316

317

// ❌ Bad - prefer-find

318

const user = users.filter(u => u.id === targetId)[0];

319

320

// ✅ Good

321

const user = users.find(u => u.id === targetId);

322

```

323

324

### Type Definition Best Practices

325

326

Rules that encourage better type definition practices.

327

328

```typescript { .api }

329

/**

330

* Prefer function types over interfaces with call signatures

331

*/

332

"prefer-function-type": RuleModule;

333

334

/**

335

* Prefer literal enum members

336

*/

337

"prefer-literal-enum-member": RuleModule;

338

339

/**

340

* Prefer initializing enum members

341

*/

342

"prefer-enum-initializers": RuleModule;

343

344

/**

345

* Require namespace keyword over module keyword

346

*/

347

"prefer-namespace-keyword": RuleModule;

348

349

/**

350

* Disallow empty interfaces

351

*/

352

"no-empty-interface": RuleModule;

353

354

/**

355

* Require consistent use of type exports

356

*/

357

"consistent-type-exports": RuleModule;

358

```

359

360

**Usage Examples:**

361

362

```typescript

363

// ❌ Bad - prefer-function-type

364

interface Handler {

365

(event: Event): void; // Single call signature

366

}

367

368

// ✅ Good

369

type Handler = (event: Event) => void;

370

371

// ❌ Bad - prefer-literal-enum-member

372

enum Status {

373

Active = getValue(), // Computed value

374

Inactive = 'inactive'

375

}

376

377

// ✅ Good

378

enum Status {

379

Active = 'active',

380

Inactive = 'inactive'

381

}

382

383

// ❌ Bad - no-empty-interface

384

interface EmptyInterface {} // No members

385

386

// ✅ Good

387

interface UserInterface {

388

name: string;

389

email: string;

390

}

391

```

392

393

### Import and Module Best Practices

394

395

Rules that promote better module organization and import patterns.

396

397

```typescript { .api }

398

/**

399

* Disallow unnecessary type imports

400

*/

401

"no-import-type-side-effects": RuleModule;

402

403

/**

404

* Require consistent type imports

405

*/

406

"consistent-type-imports": RuleModule;

407

408

/**

409

* Require consistent type exports

410

*/

411

"consistent-type-exports": RuleModule;

412

413

/**

414

* Disallow useless empty exports

415

*/

416

"no-useless-empty-export": RuleModule;

417

418

/**

419

* Disallow require statements except in import statements

420

*/

421

"no-require-imports": RuleModule;

422

```

423

424

**Usage Examples:**

425

426

```typescript

427

// ❌ Bad - consistent-type-imports

428

import { User, createUser } from './user'; // Mixed import

429

const user: User = createUser('John');

430

431

// ✅ Good

432

import type { User } from './user';

433

import { createUser } from './user';

434

const user: User = createUser('John');

435

436

// ❌ Bad - no-useless-empty-export

437

export {}; // Unnecessary empty export

438

439

// ✅ Good

440

export type { User };

441

export { createUser };

442

```

443

444

### Performance Best Practices

445

446

Rules that help avoid performance pitfalls and promote efficient code.

447

448

```typescript { .api }

449

/**

450

* Prefer regexp exec over string match for global regex

451

*/

452

"prefer-regexp-exec": RuleModule;

453

454

/**

455

* Disallow expressions that evaluate to NaN

456

*/

457

"no-loss-of-precision": RuleModule;

458

459

/**

460

* Require explicit handling of template expression types

461

*/

462

"restrict-template-expressions": RuleModule;

463

464

/**

465

* Require both operands of addition to be numbers or strings

466

*/

467

"restrict-plus-operands": RuleModule;

468

469

/**

470

* Disallow delete operator on array elements

471

*/

472

"no-array-delete": RuleModule;

473

474

/**

475

* Disallow dynamic delete operations

476

*/

477

"no-dynamic-delete": RuleModule;

478

```

479

480

**Usage Examples:**

481

482

```typescript

483

// ❌ Bad - no-array-delete

484

delete array[0]; // Creates hole in array

485

486

// ✅ Good

487

array.splice(0, 1); // Properly removes element

488

489

// ❌ Bad - restrict-plus-operands

490

const result = value + {}; // Unclear concatenation

491

492

// ✅ Good

493

const result = value + String(obj); // Explicit conversion

494

495

// ❌ Bad - no-dynamic-delete

496

delete obj[key]; // Dynamic property deletion

497

498

// ✅ Good

499

const { [key]: _, ...rest } = obj; // Destructuring removal

500

```

501

502

## Configuration Examples

503

504

### Strict Best Practices

505

506

```json

507

{

508

"rules": {

509

"@typescript-eslint/prefer-optional-chain": "error",

510

"@typescript-eslint/prefer-nullish-coalescing": "error",

511

"@typescript-eslint/prefer-as-const": "error",

512

"@typescript-eslint/prefer-readonly": "error",

513

"@typescript-eslint/require-array-sort-compare": "error",

514

"@typescript-eslint/only-throw-error": "error",

515

"@typescript-eslint/promise-function-async": "error"

516

}

517

}

518

```

519

520

### Progressive Enhancement

521

522

```json

523

{

524

"rules": {

525

"@typescript-eslint/prefer-optional-chain": "warn",

526

"@typescript-eslint/prefer-includes": "warn",

527

"@typescript-eslint/prefer-for-of": "warn",

528

"@typescript-eslint/prefer-string-starts-ends-with": "warn",

529

"@typescript-eslint/consistent-type-imports": "warn"

530

}

531

}

532

```

533

534

## Types

535

536

```typescript { .api }

537

interface RuleModule {

538

meta: {

539

type: "problem" | "suggestion" | "layout";

540

docs: {

541

description: string;

542

recommended?: boolean | string;

543

requiresTypeChecking?: boolean;

544

};

545

messages: Record<string, string>;

546

schema: JSONSchema;

547

fixable?: "code" | "whitespace";

548

hasSuggestions?: boolean;

549

};

550

create(context: RuleContext): RuleListener;

551

}

552

553

interface BestPracticeRuleOptions {

554

allowExpressions?: boolean;

555

allowTypedFunctionExpressions?: boolean;

556

allowHigherOrderFunctions?: boolean;

557

allowDirectConstAssertionInArrowFunctions?: boolean;

558

allowConciseFunctionExpressionUsage?: boolean;

559

}

560

561

interface PreferOptionalChainOptions {

562

checkAny?: boolean;

563

checkUnknown?: boolean;

564

checkString?: boolean;

565

checkNumber?: boolean;

566

checkBoolean?: boolean;

567

checkBigInt?: boolean;

568

requireNullish?: boolean;

569

}

570

```