deno_lint logodeno_lint

Showing 91 out of 120 rules

Requires overload signatures to be adjacent to each other.

Overloaded signatures which are not next to each other can lead to code which is hard to read and maintain.

Invalid:

(bar is declared in-between foo overloads)

type FooType = {
  foo(s: string): void;
  foo(n: number): void;
  bar(): void;
  foo(sn: string | number): void;
};
interface FooInterface {
  foo(s: string): void;
  foo(n: number): void;
  bar(): void;
  foo(sn: string | number): void;
}
class FooClass {
  foo(s: string): void;
  foo(n: number): void;
  bar(): void {}
  foo(sn: string | number): void {}
}
export function foo(s: string): void;
export function foo(n: number): void;
export function bar(): void {}
export function foo(sn: string | number): void {}

Valid:

(bar is declared after foo)

type FooType = {
  foo(s: string): void;
  foo(n: number): void;
  foo(sn: string | number): void;
  bar(): void;
};
interface FooInterface {
  foo(s: string): void;
  foo(n: number): void;
  foo(sn: string | number): void;
  bar(): void;
}
class FooClass {
  foo(s: string): void;
  foo(n: number): void;
  foo(sn: string | number): void {}
  bar(): void {}
}
export function foo(s: string): void;
export function foo(n: number): void;
export function foo(sn: string | number): void {}
export function bar(): void {}

ban-ts-comment

Recommended

Disallows the use of Typescript directives without a comment.

Typescript directives reduce the effectiveness of the compiler, something which should only be done in exceptional circumstances. The reason why should be documented in a comment alongside the directive.

Invalid:

// @ts-expect-error
let a: number = "I am a string";
// @ts-ignore
let a: number = "I am a string";
// @ts-nocheck
let a: number = "I am a string";

Valid:

// @ts-expect-error: Temporary workaround (see ticket #422)
let a: number = "I am a string";
// @ts-ignore: Temporary workaround (see ticket #422)
let a: number = "I am a string";
// @ts-nocheck: Temporary workaround (see ticket #422)
let a: number = "I am a string";

ban-types

Recommended

Bans the use of primitive wrapper objects (e.g. String the object is a wrapper

of string the primitive) in addition to the non-explicit Function type and the misunderstood Object type.

There are very few situations where primitive wrapper objects are desired and far more often a mistake was made with the case of the primitive type. You also cannot assign a primitive wrapper object to a primitive leading to type issues down the line. For reference, the TypeScript handbook also says we shouldn't ever use these wrapper objects.

With Function, it is better to explicitly define the entire function signature rather than use the non-specific Function type which won't give you type safety with the function.

Finally, Object and {} means "any non-nullish value" rather than "any object type". object is a good choice for a meaning of "any object type".

Invalid:

let a: Boolean;
let b: String;
let c: Number;
let d: Symbol;
let e: Function;
let f: Object;
let g: {};

Valid:

let a: boolean;
let b: string;
let c: number;
let d: symbol;
let e: () => number;
let f: object;
let g: Record<string, never>;

Warns the usage of unknown rule codes in ignore directives

We sometimes have to suppress and ignore lint errors for some reasons. We can do so using ignore directives with rule names that should be ignored like so:

// deno-lint-ignore no-explicit-any no-unused-vars
const foo: any = 42;

This rule checks for the validity of the specified rule names (i.e. whether deno_lint provides the rule or not).

Invalid:

// typo
// deno-lint-ignore eq-eq-e
console.assert(x == 42);

// unknown rule name
// deno-lint-ignore UNKNOWN_RULE_NAME
const b = "b";

Valid:

// deno-lint-ignore eq-eq-eq
console.assert(x == 42);

// deno-lint-ignore no-unused-vars
const b = "b";

Requires deno-lint-ignore to be annotated with one or more rule names.

Ignoring all rules can mask unexpected or future problems. Therefore you need to explicitly specify which rule(s) are to be ignored.

Invalid:

// deno-lint-ignore
export function duplicateArgumentsFn(a, b, a) {}

Valid:

// deno-lint-ignore no-dupe-args
export function duplicateArgumentsFn(a, b, a) {}

Warns unused ignore directives

We sometimes have to suppress and ignore lint errors for some reasons and we can do so using ignore directives.

In some cases, however, like after refactoring, we may end up having ignore directives that are no longer necessary. Such superfluous ignore directives are likely to confuse future code readers, and to make matters worse, might hide future lint errors unintentionally. To prevent such situations, this rule detects unused, superfluous ignore directives.

Invalid:

// Actually this line is valid since `export` means "used",
// so this directive is superfluous
// deno-lint-ignore no-unused-vars
export const foo = 42;

Valid:

export const foo = 42;

button-has-type

RecommendedFresh

Checks that a <button> JSX element has a valid type attribute. The default

value is "submit" which is often not the desired behavior.

Invalid:

<button />
<button type="foo" />
<button type={condition ? "foo" : "bar"} />
<button type={foo} />
<button type={2} />

Valid:

<button type="submit" />
<button type="button" />
<button type="reset" />
<button type={condition ? "button" : "submit"} />

Verifies the correct usage of constructors and calls to super().

Defined constructors of derived classes (e.g. class A extends B) must always call super(). Classes which extend non-constructors (e.g. class A extends null) must not have a constructor.

Invalid:

class A {}
class Z {
  constructor() {}
}

class B extends Z {
  constructor() {} // missing super() call
}
class C {
  constructor() {
    super(); // Syntax error
  }
}
class D extends null {
  constructor() {} // illegal constructor
}
class E extends null {
  constructor() { // illegal constructor
    super();
  }
}

Valid:

class A {}
class B extends A {}
class C extends A {
  constructor() {
    super();
  }
}
class D extends null {}

for-direction

Recommended

Requires for loop control variables to increment in the correct direction

Incrementing for loop control variables in the wrong direction leads to infinite loops. This can occur through incorrect initialization, bad continuation step logic or wrong direction incrementing of the loop control variable.

Invalid:

// Infinite loop
for (let i = 0; i < 2; i--) {}

Valid:

for (let i = 0; i < 2; i++) {}

Checks correct naming for named fresh middleware export

Files inside the routes/ folder can export middlewares that run before any rendering happens. They are expected to be available as a named export called handler. This rule checks for when the export was incorrectly named handlers instead of handler.

Invalid:

export const handlers = {
  GET() {},
  POST() {},
};
export function handlers() {}
export async function handlers() {}

Valid:

export const handler = {
  GET() {},
  POST() {},
};
export function handler() {}
export async function handler() {}

Disallows event handlers in fresh server components

Components inside the routes/ folder in a fresh app are exclusively rendered on the server. They are not rendered in the client and setting an event handler will have no effect.

Note that this rule only applies to server components inside the routes/ folder, not to fresh islands or any other components.

Invalid:

<button onClick={() => {}} />
<button onclick={() => {}} />
<my-custom-element foo={() => {}} />

Valid:

<button />
<my-custom-element />

getter-return

Recommended

Requires all property getter functions to return a value

Getter functions return the value of a property. If the function returns no value then this contract is broken.

Invalid:

let foo = {
  get bar() {},
};

class Person {
  get name() {}
}

Valid:

let foo = {
  get bar() {
    return true;
  },
};

class Person {
  get name() {
    return "alice";
  }
}

Enforce a consistent JSX boolean value style. Passing true as the boolean

value can be omitted with the shorthand syntax.

Invalid:

const foo = <Foo isFoo={true} />;
const foo = <Foo isFoo={false} />;

Valid:

const foo = <Foo isFoo />;
const foo = <Foo isFoo={false} />;

jsx-curly-braces

Recommended

Ensure consistent use of curly braces around JSX expressions.

Invalid:

const foo = <Foo foo=<div /> />;
const foo = <Foo str={"foo"} />;
const foo = <div>{"foo"}</div>;

Valid:

const foo = <Foo foo={<div />} />;
const foo = <Foo str="foo" />;
const foo = <div>foo</div>;

jsx-key

Recommended

Ensure the key attribute is present when passing iterables into JSX. It allows

frameworks to optimize checking the order of elements.

Invalid:

const foo = [<div>foo</div>];
const foo = [<>foo</>];
[1, 2, 3].map(() => <div />);
Array.from([1, 2, 3], () => <div />);

Valid:

const foo = [<div key="a">foo</div>];
const foo = [<Fragment key="b">foo</Fragment>];
[1, 2, 3].map((x) => <div key={x} />);
Array.from([1, 2, 3], (x) => <div key={x} />);

jsx-no-children-prop

RecommendedFresh

Pass children as JSX children instead of as an attribute.

Invalid:

<div children="foo" />
<div children={[<Foo />, <Bar />]} />

Valid:

<div>foo</div>
<div><Foo /><Bar /></div>

Prevent comment strings being accidentally passed as text in JSX.

Invalid:

const foo = <div>// comment</div>;
const foo = <div>/* comment */</div>;

Valid:

const foo = <div>{/* comment */}</div>;

Using JSX children together with dangerouslySetInnerHTML is invalid as they

will be ignored.

Invalid:

<div dangerouslySetInnerHTML={{ __html: "<h1>hello</h1>" }}>
  <h1>this will never be rendered</h1>
</div>;

Valid:

<div dangerouslySetInnerHTML={{ __html: "<h1>hello</h1>" }} />;

Disallow duplicated JSX props. Later props will always overwrite earlier props

often leading to unexpected results.

Invalid:

<div id="1" id="2" />;
<App a a />;
<App a {...b} a />;

Valid:

<div id="1" />
<App a />
<App a {...b} />
<App {...b} b />

Using unescaped entities is often a coding mistake where the developer wanted to

pass a JSX element instead. This rule ensures an explicit text form must be used.

Invalid:

<div>></div>;

Valid:

<div>&gt;</div>
<div>{">"}</div>

jsx-no-useless-fragment

RecommendedFresh

Fragments are only necessary at the top of a JSX "block" and only when there are

multiple children. Fragments are not needed in other scenarios.

Invalid:

<></>
<><div /></>
<><App /></>
<p>foo <>bar</></p>

Valid:

<>{foo}</>
<><div /><div /></>
<>foo <div /></>
<p>foo bar</p>

Spreading the same expression twice is typically a mistake and causes

unnecessary computations.

Invalid:

<div {...foo} {...foo} />
<div {...foo} a {...foo} />
<Foo {...foo.bar} {...foo.bar} />

Valid:

<div {...foo} />
<div {...foo.bar} a />
<Foo {...foo.bar} />

Ensure that void elements in HTML don't have any children as that is not valid

HTML. See Void element article on MDN for more information.

Invalid:

<br>foo</br>
<img src="a.jpg">foo</img>

Valid:

<br />
<img src="a.jpg" />

Enforce conventional usage of array construction

Array construction is conventionally done via literal notation such as [] or [1, 2, 3]. Using the new Array() is discouraged as is new Array(1, 2, 3). There are two reasons for this. The first is that a single supplied argument defines the array length, while multiple arguments instead populate the array of no fixed size. This confusion is avoided when pre-populated arrays are only created using literal notation. The second argument to avoiding the Array constructor is that the Array global may be redefined.

The one exception to this rule is when creating a new array of fixed size, e.g. new Array(6). This is the conventional way to create arrays of fixed length.

Invalid:

// This is 4 elements, not a size 100 array of 3 elements
const a = new Array(100, 1, 2, 3);

const b = new Array(); // use [] instead

Valid:

const a = new Array(100);
const b = [];
const c = [1, 2, 3];

Requires that async promise executor functions are not used

Promise constructors take an executor function as an argument with resolve and reject parameters that can be used to control the state of the created Promise. This function is allowed to be async but this is generally not a good idea for several reasons:

  • If an async executor function throws an error, the error will be lost and won't cause the newly-constructed Promise to reject. This could make it difficult to debug and handle some errors.
  • If an async Promise executor function is using await, then this is usually a sign that it is not actually necessary to use the new Promise constructor and the code can be restructured to avoid the use of a promise, or the scope of the new Promise constructor can be reduced, extracting the async code and changing it to be synchronous.

Invalid:

new Promise(async function (resolve, reject) {});
new Promise(async (resolve, reject) => {});

Valid:

new Promise(function (resolve, reject) {});
new Promise((resolve, reject) => {});

Disallow await keyword inside a non-async function

Using the await keyword inside a non-async function is a syntax error. To be able to use await inside a function, the function needs to be marked as async via the async keyword

Invalid:

function foo() {
  await bar();
}

const fooFn = function foo() {
  await bar();
};

const fooFn = () => {
  await bar();
};

Valid:

async function foo() {
  await bar();
}

const fooFn = async function foo() {
  await bar();
};

const fooFn = async () => {
  await bar();
};

Requires lexical declarations (let, const, function and class) in switch

case or default clauses to be scoped with brackets.

Without brackets in the case or default block, the lexical declarations are visible to the entire switch block but only get initialized when they are assigned, which only happens if that case/default is reached. This can lead to unexpected errors. The solution is to ensure each case or default block is wrapped in brackets to scope limit the declarations.

Invalid:

switch (choice) {
  // `let`, `const`, `function` and `class` are scoped the entire switch statement here
  case 1:
    let a = "choice 1";
    break;
  case 2:
    const b = "choice 2";
    break;
  case 3:
    function f() {
      return "choice 3";
    }
    break;
  default:
    class C {}
}

Valid:

switch (choice) {
  // The following `case` and `default` clauses are wrapped into blocks using brackets
  case 1: {
    let a = "choice 1";
    break;
  }
  case 2: {
    const b = "choice 2";
    break;
  }
  case 3: {
    function f() {
      return "choice 3";
    }
    break;
  }
  default: {
    class C {}
  }
}

no-class-assign

Recommended

Disallows modifying variables of class declarations

Declaring a class such as class A {}, creates a variable A. Like any variable this can be modified or reassigned. In most cases this is a mistake and not what was intended.

Invalid:

class A {}
A = 0; // reassigning the class variable itself

Valid:

class A {}
let c = new A();
c = 0; // reassigning the variable `c`

Disallows comparing against negative zero (-0).

Comparing a value directly against negative may not work as expected as it will also pass for non-negative zero (i.e. 0 and +0). Explicit comparison with negative zero can be performed using Object.is.

Invalid:

if (x === -0) {}

Valid:

if (x === 0) {}

if (Object.is(x, -0)) {}

no-cond-assign

Recommended

Disallows the use of the assignment operator, =, in conditional statements.

Use of the assignment operator within a conditional statement is often the result of mistyping the equality operator, ==. If an assignment within a conditional statement is required then this rule allows it by wrapping the assignment in parentheses.

Invalid:

let x;
if (x = 0) {
  let b = 1;
}
function setHeight(someNode) {
  do {
    someNode.height = "100px";
  } while (someNode = someNode.parentNode);
}

Valid:

let x;
if (x === 0) {
  let b = 1;
}
function setHeight(someNode) {
  do {
    someNode.height = "100px";
  } while ((someNode = someNode.parentNode));
}

Disallows the use of a constant expression in conditional test

Using a constant expression in a conditional test is often either a mistake or a temporary situation introduced during development and is not ready for production.

Invalid:

if (true) {}
if (2) {}
do {} while (x = 2); // infinite loop

Valid:

if (x) {}
if (x === 0) {}
do {} while (x === 2);

no-control-regex

Recommended

Disallows the use ascii control characters in regular expressions

Control characters are invisible characters in the ASCII range of 0-31. It is uncommon to use these in a regular expression and more often it is a mistake in the regular expression.

Invalid:

// Examples using ASCII (31) Carriage Return (hex x0d)
const pattern1 = /\x0d/;
const pattern2 = /\u000d/;
const pattern3 = new RegExp("\\x0d");
const pattern4 = new RegExp("\\u000d");

Valid:

// Examples using ASCII (32) Space (hex x20)
const pattern1 = /\x20/;
const pattern2 = /\u0020/;
const pattern3 = new RegExp("\\x20");
const pattern4 = new RegExp("\\u0020");

no-danger

Recommended

Prevent the use of dangerouslySetInnerHTML which can lead to XSS

vulnerabilities if used incorrectly.

Invalid:

const hello = <div dangerouslySetInnerHTML={{ __html: "Hello World!" }} />;

Valid:

const hello = <div>Hello World!</div>;

no-debugger

Recommended

Disallows the use of the debugger statement

debugger is a statement which is meant for stopping the javascript execution environment and start the debugger at the statement. Modern debuggers and tooling no longer need this statement and leaving it in can cause the execution of your code to stop in production.

Invalid:

function isLongString(x: string) {
  debugger;
  return x.length > 100;
}

Valid:

function isLongString(x: string) {
  return x.length > 100; // set breakpoint here instead
}

no-delete-var

Recommended

Disallows the deletion of variables

delete is used to remove a property from an object. Variables declared via var, let and const cannot be deleted (delete will return false). Setting strict mode on will raise a syntax error when attempting to delete a variable.

Invalid:

const a = 1;
let b = 2;
let c = 3;
delete a; // would return false
delete b; // would return false
delete c; // would return false

Valid:

let obj = {
  a: 1,
};
delete obj.a; // return true

Warns the usage of the deprecated - Deno APIs

The following APIs will be removed from the Deno.* namespace but have newer APIs to migrate to. See the Deno 1.x to 2.x Migration Guide for migration instructions.

  • Deno.Buffer
  • Deno.Closer
  • Deno.close()
  • Deno.Conn.rid
  • Deno.copy()
  • Deno.customInspect
  • Deno.File
  • Deno.fstatSync()
  • Deno.fstat()
  • Deno.FsWatcher.rid
  • Deno.ftruncateSync()
  • Deno.ftruncate()
  • Deno.futimeSync()
  • Deno.futime()
  • Deno.isatty()
  • Deno.Listener.rid
  • Deno.ListenTlsOptions.certFile
  • Deno.ListenTlsOptions.keyFile
  • Deno.readAllSync()
  • Deno.readAll()
  • Deno.Reader
  • Deno.ReaderSync
  • Deno.readSync()
  • Deno.read()
  • Deno.run()
  • Deno.seekSync()
  • Deno.seek()
  • Deno.serveHttp()
  • Deno.Server
  • Deno.shutdown
  • Deno.stderr.rid
  • Deno.stdin.rid
  • Deno.stdout.rid
  • Deno.TlsConn.rid
  • Deno.UnixConn.rid
  • Deno.writeAllSync()
  • Deno.writeAll()
  • Deno.Writer
  • Deno.WriterSync
  • Deno.writeSync()
  • Deno.write()
  • new Deno.FsFile()

The following APIs will be removed from the Deno.* namespace without replacement.

  • Deno.resources()
  • Deno.metrics()

no-dupe-args

Recommended

Disallows using an argument name more than once in a function signature

If you supply multiple arguments of the same name to a function, the last instance will shadow the preceding one(s). This is most likely an unintentional typo.

Invalid:

function withDupes(a, b, a) {
  console.log("I'm the value of the second a:", a);
}

Valid:

function withoutDupes(a, b, c) {
  console.log("I'm the value of the first (and only) a:", a);
}

Disallows using a class member function name more than once

Declaring a function of the same name twice in a class will cause the previous declaration(s) to be overwritten, causing unexpected behaviors.

Invalid:

class Foo {
  bar() {}
  bar() {}
}

Valid:

class Foo {
  bar() {}
  fizz() {}
}

no-dupe-else-if

Recommended

Disallows using the same condition twice in an if/else if statement

When you reuse a condition in an if/else if statement, the duplicate condition will never be reached (without unusual side-effects) meaning this is almost always a bug.

Invalid:

if (a) {}
else if (b) {}
else if (a) {} // duplicate of condition above

if (a === 5) {}
else if (a === 6) {}
else if (a === 5) {} // duplicate of condition above

Valid:

if (a) {}
else if (b) {}
else if (c) {}

if (a === 5) {}
else if (a === 6) {}
else if (a === 7) {}

no-dupe-keys

Recommended

Disallows duplicate keys in object literals.

Setting the same key multiple times in an object literal will override other assignments to that key and can cause unexpected behaviour.

Invalid:

const foo = {
  bar: "baz",
  bar: "qux",
};
const foo = {
  "bar": "baz",
  bar: "qux",
};
const foo = {
  0x1: "baz",
  1: "qux",
};

Valid:

const foo = {
  bar: "baz",
  quxx: "qux",
};

Disallows using the same case clause in a switch statement more than once

When you reuse a case test expression in a switch statement, the duplicate case will never be reached meaning this is almost always a bug.

Invalid:

const someText = "a";
switch (someText) {
  case "a": // (1)
    break;
  case "b":
    break;
  case "a": // duplicate of (1)
    break;
  default:
    break;
}

Valid:

const someText = "a";
switch (someText) {
  case "a":
    break;
  case "b":
    break;
  case "c":
    break;
  default:
    break;
}

no-empty

Recommended

Disallows the use of empty block statements.

Empty block statements are legal but often represent that something was missed and can make code less readable. This rule ignores block statements that only contain comments. This rule also ignores empty constructors and function bodies (including arrow functions).

Invalid:

if (foo) {}

while (foo) {}

switch (foo) {}

try {
  doSomething();
} catch (e) {
} finally {
}

Valid:

if (foo) {
  // empty
}

while (foo) {
  /* empty */
}

try {
  doSomething();
} catch (e) {
  // continue regardless of error
}

try {
  doSomething();
} finally {
  /* continue regardless of error */
}

Disallows using the empty character class in a regular expression

Regular expression character classes are a series of characters in brackets, e.g. [abc]. if nothing is supplied in the brackets it will not match anything which is likely a typo or mistake.

Invalid:

/^abc[]/.test("abcdefg"); // false, as `d` does not match an empty character class
"abcdefg".match(/^abc[]/); // null

Valid:

// Without a character class
/^abc/.test("abcdefg"); // true
"abcdefg".match(/^abc/); // ["abc"]

// With a valid character class
/^abc[a-z]/.test("abcdefg"); // true
"abcdefg".match(/^abc[a-z]/); // ["abcd"]

no-empty-enum

Recommended

Disallows the declaration of an empty enum

An enum with no members serves no purpose. This rule will capture these situations as either unnecessary code or a mistaken empty implementation.

Invalid:

enum Foo {}

Valid:

enum Foo {
  ONE = "ONE",
}

Disallows the declaration of an empty interface

An interface with no members serves no purpose. This rule will capture these situations as either unnecessary code or a mistaken empty implementation.

Invalid:

interface Foo {}

Valid:

interface Foo {
  name: string;
}

interface Bar {
  age: number;
}

// Using an empty interface with at least one extension are allowed.

// Using an empty interface to change the identity of Baz from type to interface.
type Baz = { profession: string };
interface Foo extends Baz {}

// Using an empty interface to extend already existing Foo declaration
// with members of the Bar interface
interface Foo extends Bar {}

// Using an empty interface as a union type
interface Baz extends Foo, Bar {}

no-empty-pattern

Recommended

Disallows the use of empty patterns in destructuring

In destructuring, it is possible to use empty patterns such as {} or [] which have no effect, most likely not what the author intended.

Invalid:

// In these examples below, {} and [] are not object literals or empty arrays,
// but placeholders for destructured variable names
const {} = someObj;
const [] = someArray;
const {a: {}} = someObj;
const [a: []] = someArray;
function myFunc({}) {}
function myFunc([]) {}

Valid:

const { a } = someObj;
const [a] = someArray;

// Correct way to default destructured variable to object literal
const { a = {} } = someObj;

// Correct way to default destructured variable to empty array
const [a = []] = someArray;

function myFunc({ a }) {}
function myFunc({ a = {} }) {}
function myFunc([a]) {}
function myFunc([a = []]) {}

no-ex-assign

Recommended

Disallows the reassignment of exception parameters

There is generally no good reason to reassign an exception parameter. Once reassigned the code from that point on has no reference to the error anymore.

Invalid:

try {
  someFunc();
} catch (e) {
  e = true;
  // can no longer access the thrown error
}

Valid:

try {
  someFunc();
} catch (e) {
  const anotherVar = true;
}

no-explicit-any

Recommended

Disallows use of the any type

Use of the any type disables the type check system around that variable, defeating the purpose of Typescript which is to provide type safe code. Additionally, the use of any hinders code readability, since it is not immediately clear what type of value is being referenced. It is better to be explicit about all types. For a more type-safe alternative to any, use unknown if you are unable to choose a more specific type.

Invalid:

const someNumber: any = "two";
function foo(): any {
  return undefined;
}

Valid:

const someNumber: string = "two";
function foo(): undefined {
  return undefined;
}

Disallows unnecessary boolean casts

In certain contexts, such as if, while or for statements, expressions are automatically coerced into a boolean. Therefore, techniques such as double negation (!!foo) or casting (Boolean(foo)) are unnecessary and produce the same result as without the negation or casting.

Invalid:

if (!!foo) {}
if (Boolean(foo)) {}
while (!!foo) {}
for (; Boolean(foo);) {}

Valid:

if (foo) {}
while (foo) {}
for (; foo;) {}

Disallows unnecessary non-null assertions

Non-null assertions are specified with an ! saying to the compiler that you know this value is not null. Specifying this operator more than once in a row, or in combination with the optional chaining operator (?) is confusing and unnecessary.

Invalid:

const foo: { str: string } | null = null;
const bar = foo!!.str;

function myFunc(bar: undefined | string) {
  return bar!!;
}
function anotherFunc(bar?: { str: string }) {
  return bar!?.str;
}

Valid:

const foo: { str: string } | null = null;
const bar = foo!.str;

function myFunc(bar: undefined | string) {
  return bar!;
}
function anotherFunc(bar?: { str: string }) {
  return bar?.str;
}

no-fallthrough

Recommended

Disallows the implicit fallthrough of case statements

Case statements without a break will execute their body and then fallthrough to the next case or default block and execute this block as well. While this is sometimes intentional, many times the developer has forgotten to add a break statement, intending only for a single case statement to be executed. This rule enforces that you either end each case statement with a break statement or an explicit comment that fallthrough was intentional. The fallthrough comment must contain one of fallthrough, falls through or fall through.

Invalid:

switch (myVar) {
  case 1:
    console.log("1");

  case 2:
    console.log("2");
}
// If myVar = 1, outputs both `1` and `2`.  Was this intentional?

Valid:

switch (myVar) {
  case 1:
    console.log("1");
    break;

  case 2:
    console.log("2");
    break;
}
// If myVar = 1, outputs only `1`

switch (myVar) {
  case 1:
    console.log("1");
    /* falls through */
  case 2:
    console.log("2");
}
// If myVar = 1, intentionally outputs both `1` and `2`

no-func-assign

Recommended

Disallows the overwriting/reassignment of an existing function

Javascript allows for the reassignment of a function definition. This is generally a mistake on the developers part, or poor coding practice as code readability and maintainability will suffer.

Invalid:

function foo() {}
foo = bar;

const a = function baz() {
  baz = "now I'm a string";
};

myFunc = existingFunc;
function myFunc() {}

Valid:

function foo() {}
const someVar = foo;

const a = function baz() {
  const someStr = "now I'm a string";
};

const anotherFuncRef = existingFunc;

let myFuncVar = function () {};
myFuncVar = bar; // variable reassignment, not function re-declaration

no-global-assign

Recommended

Disallows assignment to native Javascript objects

In Javascript, String and Object for example are native objects. Like any object, they can be reassigned, but it is almost never wise to do so as this can lead to unexpected results and difficult to track down bugs.

Invalid:

Object = null;
undefined = true;
window = {};

Disallows the assert keyword for import attributes

ES import attributes (previously called import assertions) has been changed to use the with keyword. The old syntax using assert is still supported, but deprecated.

Invalid:

import obj from "./obj.json" assert { type: "json" };
import("./obj2.json", { assert: { type: "json" } });

Valid:

import obj from "./obj.json" with { type: "json" };
import("./obj2.json", { with: { type: "json" } });

no-import-assign

Recommended

Disallows reassignment of imported module bindings

ES module import bindings should be treated as read-only since modifying them during code execution will likely result in runtime errors. It also makes for poor code readability and difficult maintenance.

Invalid:

import defaultMod, { namedMod } from "./mod.js";
import * as modNameSpace from "./mod2.js";

defaultMod = 0;
namedMod = true;
modNameSpace.someExportedMember = "hello";
modNameSpace = {};

Valid:

import defaultMod, { namedMod } from "./mod.js";
import * as modNameSpace from "./mod2.js";

// properties of bound imports may be set
defaultMod.prop = 1;
namedMod.prop = true;
modNameSpace.someExportedMember.prop = "hello";

Disallows variable or function definitions in nested blocks

Function declarations in nested blocks can lead to less readable code and potentially unexpected results due to compatibility issues in different JavaScript runtimes. This does not apply to named or anonymous functions which are valid in a nested block context.

Variables declared with var in nested blocks can also lead to less readable code. Because these variables are hoisted to the module root, it is best to declare them there for clarity. Note that variables declared with let or const are block scoped and therefore this rule does not apply to them.

Invalid:

if (someBool) {
  function doSomething() {}
}

function someFunc(someVal: number): void {
  if (someVal > 4) {
    var a = 10;
  }
}

Valid:

function doSomething() {}
if (someBool) {}

var a = 10;
function someFunc(someVal: number): void {
  var foo = true;
  if (someVal > 4) {
    let b = 10;
    const fn = function doSomethingElse() {};
  }
}

Disallows specifying invalid regular expressions in RegExp constructors

Specifying an invalid regular expression literal will result in a SyntaxError at compile time, however specifying an invalid regular expression string in the RegExp constructor will only be discovered at runtime.

Invalid:

const invalidRegExp = new RegExp(")");

Valid:

const goodRegExp = new RegExp(".");

Warns the wrong usage of triple-slash reference directives.

Deno supports the triple-slash reference directives of types, path, lib, and no-default-lib. This lint rule checks if there is an invalid, badly-formed directive because it is most likely a mistake.

Additionally, note that only the types directive is allowed in JavaScript files. This directive is useful for telling the TypeScript compiler the location of a type definition file that corresponds to a certain JavaScript file. However, even in the Deno manual of the versions prior to v1.10 (e.g. v1.9.2), there was a wrong statement describing that one should use the path directive in such cases. Actually, the types directive should be used. See the latest manual for more detail. So this rule also detects the usage of the directive other than types in JavaScript files and suggests replacing it with the types directive.

Invalid:

JavaScript

/// <reference path="./mod.d.ts" />
/// <reference no-default-lib="true" />
/// <reference foo="bar" />

// ... the rest of the JavaScript ...

TypeScript

/// <reference foo="bar" />

// ... the rest of the TypeScript ...

Valid:

JavaScript

/// <reference types="./mod.d.ts" />
/// <reference lib="es2017.string" />

// ... the rest of the JavaScript ...

TypeScript

/// <reference types="./mod.d.ts" />
/// <reference path="./mod.d.ts" />
/// <reference lib="es2017.string" />
/// <reference no-default-lib="true" />

// ... the rest of the TypeScript ...

Disallows the use of non-space or non-tab whitespace characters

Non-space or non-tab whitespace characters can be very difficult to spot in your code as editors will often render them invisibly. These invisible characters can cause issues or unexpected behaviors. Sometimes these characters are added inadvertently through copy/paste or incorrect keyboard shortcuts.

The following characters are disallowed:

\u000B - Line Tabulation (\v) - <VT>
\u000C - Form Feed (\f) - <FF>
\u00A0 - No-Break Space - <NBSP>
\u0085 - Next Line
\u1680 - Ogham Space Mark
\u180E - Mongolian Vowel Separator - <MVS>
\ufeff - Zero Width No-Break Space - <BOM>
\u2000 - En Quad
\u2001 - Em Quad
\u2002 - En Space - <ENSP>
\u2003 - Em Space - <EMSP>
\u2004 - Tree-Per-Em
\u2005 - Four-Per-Em
\u2006 - Six-Per-Em
\u2007 - Figure Space
\u2008 - Punctuation Space - <PUNCSP>
\u2009 - Thin Space
\u200A - Hair Space
\u200B - Zero Width Space - <ZWSP>
\u2028 - Line Separator
\u2029 - Paragraph Separator
\u202F - Narrow No-Break Space
\u205f - Medium Mathematical Space
\u3000 - Ideographic Space

To fix this linting issue, replace instances of the above with regular spaces, tabs or new lines. If it's not obvious where the offending character(s) are try retyping the line from scratch.

no-misused-new

Recommended

Disallows defining constructors for interfaces or new for classes

Specifying a constructor for an interface or defining a new method for a class is incorrect and should be avoided.

Invalid:

class C {
  new(): C;
}

interface I {
  constructor(): void;
}

Valid:

class C {
  constructor() {}
}

interface I {
  new (): C;
}

no-namespace

Recommended

Disallows the use of namespace and module keywords in TypeScript code.

namespace and module are both thought of as outdated keywords to organize the code. Instead, it is generally preferable to use ES2015 module syntax (e.g. import/export).

However, this rule still allows the use of these keywords in the following two cases:

  • they are used for defining "ambient" namespaces along with declare keywords
  • they are written in TypeScript's type definition files: .d.ts

Invalid:

// foo.ts
module mod {}
namespace ns {}
// bar.d.ts
// all usage of `module` and `namespace` keywords are allowed in `.d.ts`

Valid:

// foo.ts
declare global {}
declare module mod1 {}
declare module "mod2" {}
declare namespace ns {}
// bar.d.ts
module mod1 {}
namespace ns1 {}
declare global {}
declare module mod2 {}
declare module "mod3" {}
declare namespace ns2 {}

no-new-symbol

Recommended

Disallows the use of new operators with built-in Symbols

Symbols are created by being called as a function, but we sometimes call it with the new operator by mistake. This rule detects such wrong usage of the new operator.

Invalid:

const foo = new Symbol("foo");

Valid:

const foo = Symbol("foo");

function func(Symbol: typeof SomeClass) {
  // This `Symbol` is not built-in one
  const bar = new Symbol();
}

no-node-globals

Recommended

Disallows the use of NodeJS global objects.

NodeJS exposes a set of global objects that differs from deno (and the web), so code should not assume they are available. Instead, import the objects from their defining modules as needed.

Invalid:

// foo.ts
const buf = Buffer.from("foo", "utf-8"); // Buffer is not a global object in deno

Valid:

// foo.ts
import { Buffer } from "node:buffer";

const foo = Buffer.from("foo", "utf-8");

no-obj-calls

Recommended

Disallows calling built-in global objects like functions

The following built-in objects should not be invoked like functions, even though they look like constructors:

  • Math
  • JSON
  • Reflect
  • Atomics

Calling these as functions would result in runtime errors. This rule statically prevents such wrong usage of them.

Invalid:

const math = Math();
const newMath = new Math();

const json = JSON();
const newJSON = new JSON();

const reflect = Reflect();
const newReflect = new Reflect();

const atomics = Atomics();
const newAtomics = new Atomics();

Valid:

const area = (radius: number): number => Math.PI * radius * radius;

const parsed = JSON.parse("{ foo: 42 }");

const x = Reflect.get({ x: 1, y: 2 }, "x");

const first = Atomics.load(foo, 0);

no-octal

Recommended

Disallows expressing octal numbers via numeric literals beginning with 0

Octal numbers can be expressed via numeric literals with leading 0 like 042, but this expression often confuses programmers. That's why ECMAScript's strict mode throws SyntaxError for the expression.

Since ES2015, the other prefix 0o has been introduced as an alternative. This new one is always encouraged to use in today's code.

Invalid:

const a = 042;
const b = 7 + 042;

Valid:

const a = 0o42;
const b = 7 + 0o42;
const c = "042";

Disallows the use of NodeJS process global.

NodeJS and Deno expose process global but they are hard to statically analyze by tools, so code should not assume they are available. Instead, import process from "node:process".

Invalid:

// foo.ts
const foo = process.env.FOO;

Valid:

// foo.ts
import process from "node:process";

const foo = process.env.FOO;

Disallows the use of Object.prototype builtins directly

If objects are created via Object.create(null) they have no prototype specified. This can lead to runtime errors when you assume objects have properties from Object.prototype and attempt to call the following methods:

  • hasOwnProperty
  • isPrototypeOf
  • propertyIsEnumerable

Instead, it's always encouraged to call these methods from Object.prototype explicitly.

Invalid:

const a = foo.hasOwnProperty("bar");
const b = foo.isPrototypeOf("bar");
const c = foo.propertyIsEnumerable("bar");

Valid:

const a = Object.prototype.hasOwnProperty.call(foo, "bar");
const b = Object.prototype.isPrototypeOf.call(foo, "bar");
const c = Object.prototype.propertyIsEnumerable.call(foo, "bar");

no-redeclare

Recommended

Disallows redeclaration of variables, functions, parameters with the same name.

JavaScript allows us to redeclare variables with the same name using var, but redeclaration should not be used since it can make variables hard to trace.

In addition, this lint rule disallows redeclaration using let or const as well, although ESLint allows. This is useful because we can notice a syntax error before actually running the code.

As for functions and parameters, JavaScript just treats these as runtime errors, throwing SyntaxError when being run. It's also beneficial to detect this sort of errors statically.

Invalid:

var a = 3;
var a = 10;

let b = 3;
let b = 10;

const c = 3;
const c = 10;

function d() {}
function d() {}

function e(arg: number) {
  var arg: number;
}

function f(arg: number, arg: string) {}

Valid:

var a = 3;
function f() {
  var a = 10;
}

if (foo) {
  let b = 2;
} else {
  let b = 3;
}

no-regex-spaces

Recommended

Disallows multiple spaces in regular expression literals.

Multiple spaces in regular expression literals are generally hard to read when the regex gets complicated. Instead, it's better to use only one space character and specify how many times spaces should appear with the {n} syntax, for example:

// Multiple spaces in the regex literal are harder to understand how many
// spaces are expected to be matched
const re = /foo   bar/;

// Instead use `{n}` syntax for readability
const re = /foo {3}var/;

Invalid:

const re1 = /  /;
const re2 = /foo  bar/;
const re3 = / a b  c d /;
const re4 = /foo  {3}bar/;

const re5 = new RegExp("  ");
const re6 = new RegExp("foo  bar");
const re7 = new RegExp(" a b  c d ");
const re8 = new RegExp("foo  {3}bar");

Valid:

const re1 = /foo/;
const re2 = / /;
const re3 = / {3}/;
const re4 = / +/;
const re5 = / ?/;
const re6 = / */;

const re7 = new RegExp("foo");
const re8 = new RegExp(" ");
const re9 = new RegExp(" {3}");
const re10 = new RegExp(" +");
const re11 = new RegExp(" ?");
const re12 = new RegExp(" *");

no-self-assign

Recommended

Disallows self assignments

Self assignments like a = a; have no effect at all. If there are self assignments in the code, most likely it means that the author is still in the process of refactoring and there's remaining work they have to do.

Invalid:

a = a;
[a] = [a];
[a, b] = [a, b];
[a, b] = [a, c];
[a, ...b] = [a, ...b];
a.b = a.b;

Valid:

let a = a;
a += a;
a = [a];
[a, b] = [b, a];
a.b = a.c;

no-setter-return

Recommended

Disallows returning values from setters.

Setters are supposed to be used for setting some value to the property, which means that returning a value from a setter makes no sense. In fact, returned values are ignored and cannot ever be used at all although returning a value from a setter produces no error. This is why static check for this mistake by the linter is quite beneficial.

Note that returning without a value is allowed; this is a useful technique to do early-return from a function.

Invalid:

const a = {
  set foo(x: number) {
    return "something";
  },
};

class B {
  private set foo(x: number) {
    return "something";
  }
}

const c = {
  set foo(x: boolean) {
    if (x) {
      return 42;
    }
  },
};

Valid:

// return without a value is allowed since it is used to do early-return
const a = {
  set foo(x: number) {
    if (x % 2 == 0) {
      return;
    }
  },
};

// not a setter, but a getter
class B {
  get foo() {
    return 42;
  }
}

// not a setter
const c = {
  set(x: number) {
    return "something";
  },
};

Disallows shadowing of restricted names.

The following (a) properties of the global object, or (b) identifiers are "restricted" names in JavaScript:

These names are NOT reserved in JavaScript, which means that nothing prevents one from assigning other values into them (i.e. shadowing). In other words, you are allowed to use, say, undefined as an identifier or variable name. (For more details see MDN)

function foo() {
  const undefined = "bar";
  console.log(undefined); // output: "bar"
}

Of course, shadowing like this most likely confuse other developers and should be avoided. This lint rule detects and warn them.

Invalid:

const undefined = 42;

function NaN() {}

function foo(Infinity) {}

const arguments = () => {};

try {
} catch (eval) {}

Valid:

// If not assigned a value, `undefined` may be shadowed
const undefined;

const Object = 42;

function foo(a: number, b: string) {}

try {
} catch (e) {}

no-this-alias

Recommended

Disallows assigning variables to this.

In most cases, storing a reference to this in a variable could be avoided by using arrow functions properly, since they establish this based on the scope where the arrow function is defined.

Let's take a look at a concrete example:

const obj = {
  count: 0,
  doSomethingLater() {
    setTimeout(function () { // this function executes on the global scope; `this` evalutes to `globalThis`
      this.count++;
      console.log(this.count);
    }, 300);
  },
};

obj.doSomethingLater();
// `NaN` is printed, because the property `count` is not in the global scope.

In the above example, this in the function passed to setTimeout evaluates to globalThis, which results in the expected value 1 not being printed.

If you wanted to work around it without arrow functions, you would store a reference to this in another variable:

const obj = {
  count: 0,
  doSomethingLater() {
    const self = this; // store a reference to `this` in `self`
    setTimeout(function () {
      // use `self` instead of `this`
      self.count++;
      console.log(self.count);
    }, 300);
  },
};

obj.doSomethingLater();
// `1` is printed as expected

But in this case arrow functions come in handy. With arrow functions, the code becomes way clearer and easier to understand:

const obj = {
  count: 0,
  doSomethingLater() {
    setTimeout(() => { // pass an arrow function
      // `this` evaluates to `obj` here
      this.count++;
      console.log(this.count);
    }, 300);
  },
};

obj.doSomethingLater();
// `1` is printed as expected

This example is taken from MDN.

Invalid:

const self = this;

function foo() {
  const self = this;
}

const bar = () => {
  const self = this;
};

Valid:

const self = "this";

const [foo] = this;

Disallows use of this or super before calling super() in constructors.

The access to this or super before calling super() in the constructor of derived classes leads to ReferenceError. To prevent it, this lint rule checks if there are accesses to this or super before calling super() in constructors.

Invalid:

class A extends B {
  constructor() {
    this.foo = 0;
    super();
  }
}

class C extends D {
  constructor() {
    super.foo();
    super();
  }
}

Valid:

class A extends B {
  constructor() {
    super();
    this.foo = 0;
  }
}

class C extends D {
  constructor() {
    super();
    super.foo();
  }
}

class E {
  constructor() {
    this.foo = 0;
  }
}

no-unreachable

Recommended

Disallows the unreachable code after the control flow statements.

Because the control flow statements (return, throw, break and continue) unconditionally exit a block of code, any statements after them cannot be executed.

Invalid:

function foo() {
  return true;
  console.log("done");
}
function bar() {
  throw new Error("Oops!");
  console.log("done");
}
while (value) {
  break;
  console.log("done");
}
throw new Error("Oops!");
console.log("done");
function baz() {
  if (Math.random() < 0.5) {
    return;
  } else {
    throw new Error();
  }
  console.log("done");
}
for (;;) {}
console.log("done");

Valid

function foo() {
  return bar();
  function bar() {
    return 1;
  }
}

Disallows the use of control flow statements within finally blocks.

Use of the control flow statements (return, throw, break and continue) overrides the usage of any control flow statements that might have been used in the try or catch blocks, which is usually not the desired behaviour.

Invalid:

let foo = function () {
  try {
    return 1;
  } catch (err) {
    return 2;
  } finally {
    return 3;
  }
};
let foo = function () {
  try {
    return 1;
  } catch (err) {
    return 2;
  } finally {
    throw new Error();
  }
};

Valid:

let foo = function () {
  try {
    return 1;
  } catch (err) {
    return 2;
  } finally {
    console.log("hola!");
  }
};

Disallows the usage of negation operator ! as the left operand of relational

operators.

! operators appearing in the left operand of the following operators will sometimes cause an unexpected behavior because of the operator precedence:

  • in operator
  • instanceof operator

For example, when developers write a code like !key in someObject, most likely they want it to behave just like !(key in someObject), but actually it behaves like (!key) in someObject. This lint rule warns such usage of ! operator so it will be less confusing.

Invalid:

if (!key in object) {}
if (!foo instanceof Foo) {}

Valid:

if (!(key in object)) {}
if (!(foo instanceof Foo)) {}
if ((!key) in object) {}
if ((!foo) instanceof Foo) {}

no-unused-labels

Recommended

Disallows unused labels.

A label that is declared but never used is most likely developer's mistake. If that label is meant to be used, then write a code so that it will be used. Otherwise, remove the label.

Invalid:

LABEL1:
while (true) {
  console.log(42);
}

LABEL2:
for (let i = 0; i < 5; i++) {
  console.log(42);
}

LABEL3:
for (const x of xs) {
  console.log(x);
}

Valid:

LABEL1:
while (true) {
  console.log(42);
  break LABEL1;
}

LABEL2:
for (let i = 0; i < 5; i++) {
  console.log(42);
  continue LABEL2;
}

for (const x of xs) {
  console.log(x);
}

no-unused-vars

Recommended

Enforces all variables are used at least once.

If there are variables that are declared but not used anywhere, it's most likely because of incomplete refactoring. This lint rule detects and warns such unused variables.

Variable a is considered to be "used" if any of the following conditions are satisfied:

  • its value is read out, like console.log(a) or let otherVariable = a;
  • it's called or constructed, like a() or new a()
  • it's exported, like export const a = 42;

If a variable is just assigned to a value but never read out, then it's considered to be "not used".

let a;
a = 42;

// `a` is never read out

If you want to declare unused variables intentionally, prefix them with the underscore character _, like _a. This rule ignores variables that are prefixed with _.

Invalid:

const a = 0;

const b = 0; // this `b` is never used
function foo() {
  const b = 1; // this `b` is used
  console.log(b);
}
foo();

let c = 2;
c = 3;

// recursive function calls are not considered to be used, because only when `d`
// is called from outside the function body can we say that `d` is actually
// called after all.
function d() {
  d();
}

// `x` is never used
export function e(x: number): number {
  return 42;
}

const f = "unused variable";

Valid:

const a = 0;
console.log(a);

const b = 0;
function foo() {
  const b = 1;
  console.log(b);
}
foo();
console.log(b);

let c = 2;
c = 3;
console.log(c);

function d() {
  d();
}
d();

export function e(x: number): number {
  return x + 42;
}

export const f = "exported variable";

no-var

Recommended

Enforces the use of block scoped variables over more error prone function scoped

variables. Block scoped variables are defined using const and let keywords.

const and let keywords ensure the variables defined using these keywords are not accessible outside their block scope. On the other hand, variables defined using var keyword are only limited by their function scope.

Invalid:

var foo = "bar";

Valid:

const foo = 1;
let bar = 2;

no-window

Recommended

Disallows the use of the window object.

The window global is no longer available in Deno. Deno does not have a window and typeof window === "undefined" is often used to tell if the code is running in the browser.

Invalid:

const a = await window.fetch("https://deno.land");

const b = window.Deno.metrics();
console.log(window);

window.addEventListener("load", () => {
  console.log("Loaded.");
});

Valid:

const a1 = await fetch("https://deno.land");
const a2 = await globalThis.fetch("https://deno.land");
const a3 = await self.fetch("https://deno.land");

const b1 = Deno.metrics();
const b2 = globalThis.Deno.metrics();
const b3 = self.Deno.metrics();
console.log(globalThis);

addEventListener("load", () => {
  console.log("Loaded.");
});

no-window-prefix

Recommended

Disallows the use of Web APIs via the window object.

In most situations, the global variable window works like globalThis. For example, you could call the fetch API like window.fetch(..) instead of fetch(..) or globalThis.fetch(..). In Web Workers, however, window is not available, but instead self, globalThis, or no prefix work fine. Therefore, for compatibility between Web Workers and other contexts, it's highly recommended to not access global properties via window.

Some APIs, including window.alert, window.location and window.history, are allowed to call with window because these APIs are not supported or have different meanings in Workers. In other words, this lint rule complains about the use of window only if it's completely replaceable with self, globalThis, or no prefix.

Invalid:

const a = await window.fetch("https://deno.land");

const b = window.Deno.metrics();

Valid:

const a1 = await fetch("https://deno.land");
const a2 = await globalThis.fetch("https://deno.land");
const a3 = await self.fetch("https://deno.land");

const b1 = Deno.metrics();
const b2 = globalThis.Deno.metrics();
const b3 = self.Deno.metrics();

// `alert` is allowed to call with `window` because it's not supported in Workers
window.alert("🍣");

// `location` is also allowed
window.location.host;

no-with

Recommended

Disallows the usage of with statements.

The with statement is discouraged as it may be the source of confusing bugs and compatibility issues. For more details, see with - JavaScript | MDN.

Invalid:

with (someVar) {
  console.log("foo");
}

prefer-as-const

Recommended

Recommends using const assertion (as const) over explicitly specifying literal

types or using type assertion.

When declaring a new variable of a primitive literal type, there are three ways:

  1. adding an explicit type annotation
  2. using normal type assertion (like as "foo", or <"foo">)
  3. using const assertion (as const)

This lint rule suggests using const assertion because it will generally lead to a safer code. For more details about const assertion, see the official handbook.

Invalid:

let a: 2 = 2; // type annotation
let b = 2 as 2; // type assertion
let c = <2> 2; // type assertion
let d = { foo: 1 as 1 }; // type assertion

Valid:

let a = 2 as const;
let b = 2 as const;
let c = 2 as const;
let d = { foo: 1 as const };

let x = 2;
let y: string = "hello";
let z: number = someVariable;

prefer-const

Recommended

Recommends declaring variables with [const] over [let].

Since ES2015, JavaScript supports let and const for declaring variables. If variables are declared with let, then they become mutable; we can set other values to them afterwards. Meanwhile, if declared with const, they are immutable; we cannot perform re-assignment to them.

In general, to make the codebase more robust, maintainable, and readable, it is highly recommended to use const instead of let wherever possible. The fewer mutable variables are, the easier it should be to keep track of the variable states while reading through the code, and thus it is less likely to write buggy code. So this lint rule checks if there are let variables that could potentially be declared with const instead.

Note that this rule does not check for var variables. Instead, the no-var rule is responsible for detecting and warning var variables.

Invalid:

let a = 0;

let b = 0;
someOperation(b);

// `const` could be used instead
for (let c in someObject) {}

// `const` could be used instead
for (let d of someArray) {}

// variable that is uninitialized at first and then assigned in the same scope is NOT allowed
// because we could simply write it like `const e = 2;` instead
let e;
e = 2;

Valid:

// uninitialized variable is allowed
let a;

let b = 0;
b += 1;

let c = 0;
c = 1;

// variable that is uninitialized at first and then assigned in the same scope _two or more times_ is allowed
// because we cannot represent it with `const`
let d;
d = 2;
d = 3;

const e = 0;

// `f` is mutated through `f++`
for (let f = 0; f < someArray.length; f++) {}

// variable that is initialized (or assigned) in another scope is allowed
let g;
function func1() {
  g = 42;
}

// conditionally initialized variable is allowed
let h;
if (trueOrFalse) {
  h = 0;
}

Recommends the use of namespace keyword over module keyword when declaring

TypeScript module.

TypeScript supports the module keyword for organizing code, but this wording can lead to a confusion with the ECMAScript's module. Since TypeScript v1.5, it has provided us with the alternative keyword namespace, encouraging us to always use namespace instead whenever we write TypeScript these days. See TypeScript v1.5 release note for more details.

Invalid:

module modA {}

declare module modB {}

Valid:

namespace modA {}

// "ambient modules" are allowed
// https://www.typescriptlang.org/docs/handbook/modules.html#ambient-modules
declare module "modB";
declare module "modC" {}

require-await

Recommended

Disallows async functions that have no await expression or await using

declaration

In general, the primary reason to use async functions is to use await expressions or await using declarations inside. If an async function has neither, it is most likely an unintentional mistake.

Invalid:

async function f1() {
  doSomething();
}

const f2 = async () => {
  doSomething();
};

const f3 = async () => doSomething();

const obj = {
  async method() {
    doSomething();
  },
};

class MyClass {
  async method() {
    doSomething();
  }
}

Valid:

await asyncFunction();

function normalFunction() {
  doSomething();
}

async function f1() {
  await asyncFunction();
}

const f2 = async () => {
  await asyncFunction();
};

const f3 = async () => await asyncFunction();

async function f4() {
  for await (const num of asyncIterable) {
    console.log(num);
  }
}

async function f5() {
  using = createResource();
}

// empty functions are valid
async function emptyFunction() {}
const emptyArrowFunction = async () => {};

// generators are also valid
async function* gen() {
  console.log(42);
}

require-yield

Recommended

Disallows generator functions that have no yield.

JavaScript provides generator functions expressed as function*, where we can pause and later resume the function execution at the middle points. At these points we use the yield keyword. In other words, it makes no sense at all to create generator functions that contain no yield keyword, since such functions could be written as normal functions.

Invalid:

function* f1() {
  return "f1";
}

Valid:

function* f1() {
  yield "f1";
}

// generator function with empty body is allowed
function* f2() {}

function f3() {
  return "f3";
}

rules-of-hooks

RecommendedFresh

Ensure that React/Preact hooks are only called inside component functions and

not called conditionally. A hook is a function that starts with use prefix.

Invalid:

// BAD: Called conditionally
function Component() {
  if (cond) {
    const [count, setCount] = useState(0);
  }
  // ...
}

// BAD: Called in a loop
function Component() {
  for (let i = 0; i < 10; i++) {
    const [count, setCount] = useState(0);
  }
  // ...
}

// BAD: Called after conditional return
function Component() {
  if (cond) {
    return;
  }

  const [count, setCount] = useState(0);
  // ...
}

// BAD: Called inside event handler
function Component() {
  function onClick() {
    const [count, setCount] = useState(0);
  }

  return <button onClick={onClick}>click me</button>;
}

// BAD: Called inside useMemo
function Component() {
  const value = useMemo(() => {
    const [count, setCount] = useState(0);
    return count;
  });
}

// BAD: Called inside try/catch
function Component() {
  try {
    const [count, setCount] = useState(0);
  } catch {
    const [count, setCount] = useState(0);
  }

  // ...
}

Valid:

function Component() {
  const [count, setCount] = useState(0);
  // ...
}

function useCustomHook() {
  const [count, setCount] = useState(0);
  // ...
}

use-isnan

Recommended

Disallows comparisons to NaN.

Because NaN is unique in JavaScript by not being equal to anything, including itself, the results of comparisons to NaN are confusing:

  • NaN === NaN or NaN == NaN evaluate to false
  • NaN !== NaN or NaN != NaN evaluate to true

Therefore, this rule makes you use the isNaN() or Number.isNaN() to judge the value is NaN or not.

Invalid:

if (foo == NaN) {
  // ...
}

if (foo != NaN) {
  // ...
}

switch (NaN) {
  case foo:
    // ...
}

switch (foo) {
  case NaN:
    // ...
}

Valid:

if (isNaN(foo)) {
  // ...
}

if (!isNaN(foo)) {
  // ...
}

valid-typeof

Recommended

Restricts the use of the typeof operator to a specific set of string literals.

When used with a value the typeof operator returns one of the following strings:

  • "undefined"
  • "object"
  • "boolean"
  • "number"
  • "string"
  • "function"
  • "symbol"
  • "bigint"

This rule disallows comparison with anything other than one of these string literals when using the typeof operator, as this likely represents a typing mistake in the string. The rule also disallows comparing the result of a typeof operation with any non-string literal value, such as undefined, which can represent an inadvertent use of a keyword instead of a string. This includes comparing against string variables even if they contain one of the above values as this cannot be guaranteed. An exception to this is comparing the results of two typeof operations as these are both guaranteed to return on of the above strings.

Invalid:

// typo
typeof foo === "strnig";
typeof foo == "undefimed";
typeof bar != "nunber";
typeof bar !== "fucntion";

// compare with non-string literals
typeof foo === undefined;
typeof bar == Object;
typeof baz === anotherVariable;
typeof foo == 5;

Valid:

typeof foo === "undefined";
typeof bar == "object";
typeof baz === "string";
typeof bar === typeof qux;