Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions lint/rules/no-await-in-promise-methods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
tags: [recommended]
---

Disallows using `await` on elements passed to `Promise.all()`,
`Promise.allSettled()`, `Promise.any()`, and `Promise.race()`.

These methods accept an iterable of promises and await them concurrently.
Awaiting an element _inside_ the array passed to them defeats that purpose: the
awaited promise is resolved sequentially before the method ever runs, so you
lose the concurrency the method is meant to provide. It is almost always a
mistake.

**Invalid:**

```typescript
await Promise.all([await foo(), bar()]);
await Promise.allSettled([await foo()]);
await Promise.any([await foo(), bar()]);
await Promise.race([await foo(), bar()]);
```

**Valid:**

```typescript
await Promise.all([foo(), bar()]);
await Promise.allSettled([foo(), bar()]);
await Promise.any([foo(), bar()]);
await Promise.race([foo(), bar()]);
```
39 changes: 39 additions & 0 deletions lint/rules/no-empty-file.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
tags: [recommended]
---

Disallows empty files.

A file that contains no code — only whitespace, comments, or a directive
prologue such as `"use strict"` — serves no purpose and is usually the result of
a mistake, such as an incomplete refactor or a bad merge. Either add some code
to the file or delete it.

A file consisting solely of a triple-slash reference directive (for example
`/// <reference types="..." />`) is allowed, since such directives are
meaningful.

**Invalid:**

```typescript
// (a file containing only this comment)
```

```typescript
"use strict";
```

```typescript
{
}
```

**Valid:**

```typescript
const x = 0;
```

```typescript
/// <reference types="./types.d.ts" />
```
28 changes: 28 additions & 0 deletions lint/rules/no-invalid-fetch-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
tags: [recommended]
---

Disallows passing a `body` to `fetch()` or `new Request()` when the method is
`GET` or `HEAD`.

The HTTP `GET` and `HEAD` methods must not have a request body. Supplying a
`body` together with one of these methods (including the default method, which
is `GET`) throws a `TypeError` at runtime, so it is always a mistake.

**Invalid:**

```typescript
fetch(url, { body: "foo" });
fetch(url, { method: "GET", body: "foo" });
fetch(url, { method: "HEAD", body: "foo" });
new Request(url, { body: "foo" });
```

**Valid:**

```typescript
fetch(url, { method: "POST", body: "foo" });
fetch(url, { method: "GET" });
fetch(url, { body: undefined });
new Request(url, { method: "PUT", body: "foo" });
```
32 changes: 32 additions & 0 deletions lint/rules/no-invalid-remove-event-listener.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
tags: [recommended]
---

Disallows passing inline or bound functions to `removeEventListener()`.

`removeEventListener()` removes a listener only if it receives a reference to
the _same_ function that was passed to `addEventListener()`. An inline function
expression, an arrow function, or a `.bind()` call each create a brand-new
function every time they are evaluated, so they can never match a
previously-added listener and the call silently does nothing.

To remove a listener, keep a reference to the exact function that was added and
pass that.

**Invalid:**

```typescript
el.removeEventListener("click", () => {});
el.removeEventListener("click", function () {});
el.removeEventListener("click", handler.bind(this));
```

**Valid:**

```typescript
el.removeEventListener("click", handler);

const bound = handler.bind(this);
el.addEventListener("click", bound);
el.removeEventListener("click", bound);
```
32 changes: 32 additions & 0 deletions lint/rules/no-new-array.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
tags: [recommended]
---

Disallows the use of the `Array` constructor with a single argument.

`new Array(n)` and `Array(n)` are ambiguous and error-prone. With a single
numeric argument the constructor creates a sparse array of that _length_, while
with any other single argument it creates an array containing that one
_element_. This makes the intent unclear to readers and is a common source of
bugs.

Use an array literal when you want a list of elements, or
`Array.from({ length:
n })` when you want an array of a given length.

**Invalid:**

```typescript
const a = new Array(1);
const b = new Array(foo);
const c = new Array(...bar);
```

**Valid:**

```typescript
const a = [1];
const b = Array.from({ length: 10 });
const c = new Array();
const d = new Array(1, 2, 3);
```
28 changes: 28 additions & 0 deletions lint/rules/no-single-promise-in-promise-methods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
tags: [recommended]
---

Disallows passing a single-element array to `Promise.all()`, `Promise.any()`,
and `Promise.race()`.

Wrapping a single promise in one of these methods is pointless: the result is
just the awaited value (possibly wrapped in a one-element array for
`Promise.all`), but with added overhead and noise. Await the promise directly
instead.

**Invalid:**

```typescript
await Promise.all([promise]);
await Promise.any([promise]);
await Promise.race([promise]);
```

**Valid:**

```typescript
await promise;
await Promise.all([promise1, promise2]);
await Promise.all(promises);
await Promise.race([...promises]);
```
41 changes: 41 additions & 0 deletions lint/rules/no-thenable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
tags: [recommended]
---

Disallows declaring objects, classes, and modules with a member named `then`.

The presence of a `then` method makes a value _thenable_, meaning JavaScript
treats it like a promise. This leads to surprising behavior: such an object will
be unwrapped when returned from an `async` function or passed to
`Promise.resolve()`, and the `then` method may be invoked at unexpected times.
Naming a property `then` is almost always unintentional.

**Invalid:**

```typescript
const foo = {
then() {},
};

class Foo {
then() {}
}

export function then() {}

Object.defineProperty(foo, "then", { value: () => {} });
```

**Valid:**

```typescript
const foo = {
then_() {},
};

class Foo {
isThen() {}
}

export function isThenable() {}
```
31 changes: 31 additions & 0 deletions lint/rules/no-unnecessary-await.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
tags: [recommended]
---

Disallows awaiting a value that is not a thenable.

The `await` keyword is only meaningful when applied to a `Promise` (or another
thenable). Awaiting a value that can never be a thenable — such as a literal, an
array, an object, or a newly created class instance — has no effect other than
delaying execution by a microtask, and usually indicates a misunderstanding or a
leftover from refactoring.

**Invalid:**

```typescript
async function foo() {
await 5;
await "string";
await [];
await {};
}
```

**Valid:**

```typescript
async function foo() {
await bar();
await Promise.resolve(5);
}
```
24 changes: 24 additions & 0 deletions lint/rules/no-useless-fallback-in-spread.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
tags: [recommended]
---

Disallows useless empty-object fallbacks when spreading into an object literal.

Spreading a nullish value (`null` or `undefined`) into an object literal is a
no-op, so guarding a spread with `|| {}` or `?? {}` accomplishes nothing. The
fallback only adds noise and can be removed.

**Invalid:**

```typescript
const merged = { ...(foo || {}) };
const merged2 = { ...(foo ?? {}) };
```

**Valid:**

```typescript
const merged = { ...foo };
const merged2 = { ...(foo || { a: 1 }) };
const arr = [...(foo || [])];
```
40 changes: 40 additions & 0 deletions lint/rules/no-useless-length-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
tags: [recommended]
---

Disallows redundant array length checks combined with `Array#every()` or
`Array#some()`.

`Array#every()` returns `true` for an empty array, and `Array#some()` returns
`false` for an empty array. Because of this:

- `array.length === 0 || array.every(...)` is equivalent to just
`array.every(...)`.
- `array.length !== 0 && array.some(...)` is equivalent to just
`array.some(...)`.

The explicit length check is redundant and can be removed.

**Invalid:**

```typescript
if (array.length === 0 || array.every((x) => x > 0)) {
}

if (array.length !== 0 && array.some((x) => x > 0)) {
}
```

**Valid:**

```typescript
if (array.every((x) => x > 0)) {
}

if (array.some((x) => x > 0)) {
}

// A length check combined with a different condition is fine.
if (array.length === 0 || other) {
}
```
29 changes: 29 additions & 0 deletions lint/rules/prefer-set-size.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
tags: [recommended]
---

Prefers using `Set#size` over converting a `Set` to an array to read its
`length`.

Converting a `Set` to an array with the spread operator or `Array.from()` just
to read `.length` allocates an unnecessary array and is less readable. The
`Set#size` property gives the same count directly and more efficiently.

**Invalid:**

```typescript
const size = [...new Set(array)].length;
const size2 = Array.from(new Set(array)).length;

const set = new Set(array);
const size3 = [...set].length;
```

**Valid:**

```typescript
const size = new Set(array).size;

const set = new Set(array);
const size2 = set.size;
```
Loading