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
```