Lint rules

Every rule built into bun lint

bun lint has 1,021 rules built in. Turn one on in bunfig.toml, or read about it in the terminal with bun lint --explain <rule>.

1,021 rules

ESLint 218 rules

accessor-pairs

Require a getter for every setter in objects and classes

example.js1
const user = {
  set name(value) {
    this._name = value.trim();
  },
};

array-callback-returnhas suggestions

Require callbacks of array methods such as map and filter to return a value

example.js1
const names = users.map(user => {
  user.name.trim();
});

arrow-body-stylefixable

Enforce one style for the braces around arrow function bodies

example.js1
const double = items.map(item => {
  return item * 2;
});

block-scoped-var

Disallow using a var outside the block it is declared in

example.js1
function label(user) {
  if (user.admin) {
    var prefix = "Admin: ";
  }
  return prefix + user.name; // "undefinedAda" for everyone else
}

callback-returndeprecated

Require return when calling a callback

example.js1
function load(path, callback) {
  readFile(path, (err, text) => {
    if (err) {
      callback(err);
    }
    callback(null, JSON.parse(text));
  });
}

camelcase

Require camelCase names for variables, functions and properties

example.js3
const first_name = "Ada";

function full_name(user) {
  return { display_name: user.first + " " + user.last };
}

capitalized-commentsfixable

Require comments to start with a capital letter, or forbid it

example.js1
// retry once, then give up
await send(message);

class-methods-use-this

Require class methods to use this

example.js1
class Cart {
  formatPrice(cents) {
    return "$" + (cents / 100).toFixed(2);
  }
}

complexity

Enforce a maximum number of branches in a function

example.js1
function shipping(order) {
  if (order.total > 100) {
    return 0;
  } else if (order.express) {
    return 15;
  } else if (order.country !== "US") {
    return 25;
  }
  return 5;
}

consistent-return

Require every return in a function to have a value, or none of them

example.js1
function findUser(id) {
  if (!id) {
    return;
  }
  return users.get(id);
}

consistent-this

Enforce one name for variables that hold this

example.js1
function Timer() {
  const self = this;
  setInterval(function () {
    self.tick();
  }, 1000);
}

constructor-superon by default

Require super() to be called exactly once in the constructor of a derived class

example.js1
class Admin extends User {
  constructor(name) {
    this.name = name;
  }
}

curlyfixable

Require braces around the bodies of if, else and loops

example.js2
if (!user) redirect("/login");

for (const item of items) total += item.price;

default-case

Require a default case in every switch

example.js1
switch (status) {
  case "open":
    show();
    break;
  case "closed":
    hide();
    break;
}

default-case-last

Require default to be the last clause of a switch

example.js1
switch (level) {
  default:
    log(message);
    break;
  case "error":
    alert(message);
    break;
}

default-param-last

Require parameters with a default value to come after the required ones

example.js1
function createUser(role = "member", name) {
  return { name, role };
}

dot-notationfixable

Require user.name instead of user["name"]

example.js1
const name = user["name"];

eqeqeqfixable

Require === and !== instead of == and !=

example.js1
if (count == 0) {
  reset();
}

for-directionon by default

Require the update clause of a for loop to move the counter toward the end of the loop

example.js1
for (let i = 0; i < items.length; i--) {
  render(items[i]);
}

func-name-matching

Require a function expression's name to match the variable or property it is assigned to

example.js3
const loadUser = function fetchUser(id) {
  return db.users.get(id);
};

api.remove = function destroy(id) {};

const handlers = {
  onSave: function save() {},
};

func-names

Require or disallow names for function expressions

example.js1
button.addEventListener("click", function () {
  save();
});

func-style

Enforce one way to define functions: declarations, or expressions assigned to variables

example.js1
function formatName(user) {
  return `${user.first} ${user.last}`;
}

getter-returnon by default

Require getters to return a value

example.js1
const user = {
  get fullName() {
    `${this.first} ${this.last}`;
  },
};

global-requiredeprecated

Require require() calls to be at the top level of the module

example.js1
function readConfig(path) {
  const fs = require("fs");
  return fs.readFileSync(path, "utf8");
}

grouped-accessor-pairs

Require the getter and setter of a property to be next to each other

example.js1
class User {
  get name() {
    return this.first;
  }
  save() {}
  set name(value) {
    this.first = value;
  }
}

guard-for-in

Require the body of a for...in loop to be guarded by an if

example.js1
for (const key in settings) {
  apply(key, settings[key]);
}

handle-callback-errdeprecated

Require callbacks to use their error parameter

example.js1
fs.readFile(path, "utf8", function (err, text) {
  render(text);
});

id-blacklistdeprecated

Disallow the identifier names you list (old name of id-denylist)

example.js4
const data = loadUser(id);

function save(user, callback) {}

const response = { data: 1 };
response.data = 2;

id-denylist

Disallow the identifier names you list

example.js4
const data = loadUser(id);

function save(user, callback) {}

const response = { data: 1 };
response.data = 2;

id-length

Enforce a minimum and a maximum length for identifier names

example.js4
const n = items.length;

const prices = items.map((x) => x.price);

try {
  save();
} catch (e) {
  report(e);
}

const point = { y: 0 };

id-match

Require identifier names to match a regular expression

example.js3
const user_name = "Ada";

function load_user(user_id) {}

init-declarations

Require or disallow a value in variable declarations

example.js1
let total;
if (cart.items.length > 0) {
  total = sum(cart.items);
}

logical-assignment-operatorsfixable

Require or disallow the logical assignment operators ||=, &&= and ??=

example.js5
options = options || {};
cache = cache ?? new Map();
enabled = enabled && isReady();
title || (title = "Untitled");
user.name = user.name || "guest";

max-classes-per-file

Enforce a maximum number of classes per file

example.js1
class User {}

class Session {}

max-depth

Enforce a maximum depth that blocks can be nested

example.js1
function notify(users) {
  for (const user of users) {
    if (user.active) {
      if (user.email) {
        send(user.email);
      }
    }
  }
}

max-lines

Enforce a maximum number of lines per file

example.js1
import { readFile } from "node:fs/promises";

const text = await readFile("notes.txt", "utf8");

console.log(text.split("\n").length);

max-lines-per-function

Enforce a maximum number of lines in a function

example.js1
function checkout(cart) {
  let total = 0;
  for (const item of cart.items) {
    total += item.price * item.count;
  }
  charge(cart.user, total);
}

max-nested-callbacks

Enforce a maximum depth that callbacks can be nested

example.js1
readConfig((config) => {
  connect(config, (db) => {
    db.query("select 1", (rows) => {
      console.log(rows);
    });
  });
});

max-params

Enforce a maximum number of parameters in function definitions

example.js1
function createUser(name, email, role, team) {
  return { name, email, role, team };
}

max-statements

Enforce a maximum number of statements in a function

example.js1
function register(form) {
  const user = parse(form);
  validate(user);
  save(user);
  sendWelcomeMail(user);
}

new-cap

Require constructor names to begin with a capital letter

example.js2
const user = new person("Ada");
const team = Team("core");

no-alert

Disallow alert, confirm, and prompt

example.js2
alert("Saved");

if (window.confirm("Delete this file?")) {
  remove(file);
}

no-array-constructorfixable

Disallow Array constructors in favor of array literals

example.js2
const empty = new Array();
const sizes = Array(10, 20, 30);

no-async-promise-executoron by default

Disallow an async function as a Promise executor

example.js1
const user = new Promise(async (resolve) => {
  const response = await fetch("/api/user");
  resolve(await response.json());
});

no-await-in-loop

Disallow await inside of loops

example.js1
async function loadAll(urls) {
  const pages = [];
  for (const url of urls) {
    pages.push(await fetch(url));
  }
  return pages;
}

no-bitwise

Disallow bitwise operators

example.js1
if (user.active & user.verified) {
  grant(user);
}

no-buffer-constructordeprecated

Disallow the deprecated Buffer() constructor

example.js2
const header = new Buffer(16);
const name = Buffer("Ada", "utf8");

no-caller

Disallow arguments.caller and arguments.callee

example.js1
const factorial = function (n) {
  return n <= 1 ? 1 : n * arguments.callee(n - 1);
};

no-case-declarationson by defaulthas suggestions

Disallow let, const, function and class declarations directly in a case clause

example.js1
switch (action.type) {
  case "add":
    const item = action.item;
    list.push(item);
    break;
  default:
    list.length = 0;
}

no-catch-shadowdeprecated

Disallow a catch parameter with the name of a variable in the scope around it

example.js1
const error = loadLastError();

try {
  save();
} catch (error) {
  console.log(error);
}

no-class-assignon by default

Disallow assigning to the name of a class

example.js1
class Logger {}

Logger = createLogger();

no-compare-neg-zeroon by defaulthas suggestions

Disallow comparing against -0

example.js1
if (offset === -0) {
  flipDirection();
}

no-cond-assignon by default

Disallow assignments in the test of if, loops and ? :

example.js1
if (user.role = "admin") {
  showSettings();
}

no-consolehas suggestions

Disallow the use of console

example.js2
console.log("loaded", items.length);

items.forEach(console.warn);

no-const-assignon by default

Disallow reassigning const, using and await using variables

example.js2
const retries = 0;

function onFailure() {
  retries += 1;
}

function reset() {
  retries = 0;
}

no-constant-binary-expressionon by default

Disallow comparisons and &&, ||, ?? that always give the same result

example.js3
const label = "Hello, " + user.name ?? "guest";

if (items === []) {
  showEmptyState();
}

const isMissing = !user.email === undefined;

no-constant-conditionon by default

Disallow conditions that are always truthy or always falsy

example.js2
if (role === "admin" || "owner") {
  showSettings();
}

const title = "" ? "Draft" : "Published";

no-constructor-return

Disallow returning a value from a constructor

example.js1
class Connection {
  constructor(url) {
    if (cache.has(url)) {
      return cache.get(url);
    }
    this.url = url;
  }
}

no-continue

Disallow continue statements

example.js1
for (const user of users) {
  if (!user.active) {
    continue;
  }
  notify(user);
}

no-control-regexon by default

Disallow control characters in regular expressions

example.js2
const lineBreak = /\x0A/;

const separator = new RegExp("\x1F");

no-debuggeron by default

Disallow debugger statements

example.js1
function total(items) {
  debugger;
  return items.reduce((sum, item) => sum + item.price, 0);
}

no-delete-varon by default

Disallow delete on variables

legacy.cjs1
var session = createSession();
delete session;

no-div-regexfixable

Disallow regular expressions that start with =

example.js1
const value = line.replace(/=\s*/, "");

no-dupe-argson by default

Disallow two parameters with the same name in a function

legacy.cjs1
function resize(width, height, width) {
  return width * height;
}

no-dupe-class-memberson by default

Disallow two class members with the same name

example.js1
class Cart {
  total() {
    return this.items.length;
  }

  total() {
    return this.items.reduce((sum, item) => sum + item.price, 0);
  }
}

no-dupe-else-ifon by default

Disallow an else if with a condition that an earlier branch already covers

example.js1
if (status === "active") {
  enable();
} else if (status === "paused") {
  pause();
} else if (status === "active") {
  archive();
}

no-dupe-keyson by default

Disallow two keys with the same name in an object literal

example.js1
const options = {
  timeout: 1000,
  retries: 3,
  timeout: 5000,
};

no-duplicate-caseon by default

Disallow two case labels with the same test in a switch

example.js1
switch (event.key) {
  case "ArrowUp":
    moveUp();
    break;
  case "ArrowDown":
    moveDown();
    break;
  case "ArrowUp":
    selectPrevious();
    break;
}

no-duplicate-imports

Disallow importing from the same module more than once in a file

example.js1
import { readFile } from "node:fs/promises";
import { join } from "node:path";
import { writeFile } from "node:fs/promises";

no-else-returnfixable

Disallow else after an if branch that always returns

example.js1
function label(user) {
  if (user.admin) {
    return "Admin";
  } else {
    return "Member";
  }
}

no-emptyon by defaulthas suggestions

Disallow empty block statements

example.js1
try {
  cache.clear();
} catch (error) {}

no-empty-character-classon by default

Disallow empty character classes [] in regular expressions

example.js1
const isHexColor = /^#[]{6}$/.test(color);

no-empty-functionhas suggestions

Disallow functions with an empty body

example.js2
function onClose() {}

const handlers = {
  onError: () => {},
};

no-empty-patternon by default

Disallow destructuring patterns that bind nothing

example.js3
const { settings: {} } = user;
const [] = items;

function render([]) {
  return "<div></div>";
}

no-empty-static-blockon by defaulthas suggestions

Disallow empty static {} blocks in classes

example.js1
class Config {
  static values = new Map();

  static {}
}

no-eq-null

Disallow comparing with null using == or !=

example.js2
if (user == null) {
  redirect("/login");
}

while (node != null) {
  node = node.next;
}

no-eval

Disallow eval()

example.js1
const settings = eval("(" + text + ")");

no-ex-assignon by default

Disallow assigning to the error parameter of a catch clause

example.js1
try {
  upload(file);
} catch (error) {
  error = new Error("Upload failed");
  report(error);
}

no-extend-native

Disallow adding properties to the prototypes of built-in objects

example.js2
Array.prototype.last = function () {
  return this[this.length - 1];
};

Object.defineProperty(String.prototype, "shout", {
  value() {
    return this.toUpperCase() + "!";
  },
});

no-extra-bindfixable

Disallow .bind() on functions that do not use this

example.js2
const greet = function () {
  return "Hello";
}.bind(user);

const log = (() => {
  console.log(this.name);
}).bind(user);

no-extra-boolean-caston by defaultfixable

Disallow !! and Boolean() where the value is converted to a boolean anyway

example.js3
if (!!user) {
  greet(user);
}

const label = Boolean(count) ? "some" : "none";
const isEmpty = !Boolean(items.length);

no-extra-labelfixable

Disallow labels on break and continue that name the innermost loop or switch

example.js1
search: for (const item of items) {
  if (item.id === id) {
    select(item);
    break search;
  }
}

no-fallthroughon by default

Disallow case clauses that run on into the next one

example.js1
switch (post.status) {
  case "draft":
    publish(post);
  case "published":
    notify(post);
    break;
}

no-func-assignon by default

Disallow assigning to the name of a function declaration

example.js1
function format(value) {
  return String(value);
}

format = value => value.toFixed(2);

no-global-assignon by default

Disallow assigning to built-in and other read-only global variables

example.js3
Promise = loadPromiseLibrary();
window = createFakeWindow();
status = "ready";

no-implicit-coercionfixable

Require Boolean(), Number() and String() instead of operator tricks like !!x and +x

example.js6
const hasItems = !!items.length;
const isMember = ~names.indexOf(user.name);
const quantity = +input.value;
const price = 1 * row.price;
const id = "" + user.id;
label += "";

no-implicit-globals

Disallow declarations and assignments that create global variables by accident

example.js2
var config = { debug: false };

function start() {
  console.log(config.debug);
}

no-implied-eval

Disallow passing a string of code to setTimeout(), setInterval() or execScript()

example.js3
setTimeout("refresh()", 1000);
setInterval("poll('" + url + "')", 5000);
window.setTimeout(`save(${id})`, 0);

no-import-assignon by default

Disallow assigning to imported bindings

example.js5
import config, { retries } from "./config.js";
import * as settings from "./settings.js";

config = {};
retries += 1;
settings.debug = true;
delete settings.debug;
Object.assign(settings, { debug: true });

no-inline-comments

Disallow comments on the same line as code

example.js3
const retries = 3; // the network is flaky
const timeout = /* seconds */ 30;
/* legacy */ connect(retries, timeout);

no-inner-declarations

Disallow function and, optionally, var declarations in nested blocks

logger.cjs1
if (process.env.DEBUG) {
  function log(message) {
    console.log(message);
  }
}

log("ready"); // TypeError unless DEBUG is set

no-invalid-regexpon by default

Disallow invalid patterns and flags in RegExp() and new RegExp()

example.js4
const letters = new RegExp("[a-z");
const digits = RegExp("(\\d+");
const twice = new RegExp("\\d+", "gg");
const unknown = new RegExp("\\d+", "x");

no-invalid-this

Disallow this where its value is undefined

example.js3
export const root = this;

function describe() {
  return this.name;
}

class Cart {
  total = 0;

  add(items) {
    items.forEach(function (item) {
      this.total += item.price;
    });
  }
}

no-irregular-whitespaceon by default

Disallow whitespace characters other than the ordinary space, tab and line break

example.js1
const total = price * quantity;

no-iterator

Disallow the __iterator__ property

example.js2
Playlist.prototype.__iterator__ = function () {
  return new SongIterator(this);
};

const iterate = playlist["__iterator__"];

no-label-var

Disallow labels that share a name with a variable

example.js1
const rows = table.rows;

rows: for (const row of rows) {
  for (const cell of row.cells) {
    if (cell.isEmpty) continue rows;
  }
  print(row);
}

no-labels

Disallow labeled statements

example.js2
let found;

search: for (const row of rows) {
  for (const cell of row.cells) {
    if (cell.value === target) {
      found = cell;
      break search;
    }
  }
}

no-lone-blocks

Disallow blocks that do nothing

example.js2
{
  loadConfig();
}

function start() {
  connect();
  {
    listen();
  }
}

no-lonely-iffixable

Require else if instead of an if alone in an else block

example.js1
if (user.isAdmin) {
  showAdminPage();
} else {
  if (user.isMember) {
    showMemberPage();
  } else {
    showLogin();
  }
}

no-loop-func

Disallow functions in loops that use a variable the loop changes

example.js1
for (var i = 0; i < buttons.length; i++) {
  buttons[i].onclick = function () {
    select(i); // always buttons.length
  };
}

no-loss-of-precisionon by default

Disallow number literals that cannot be stored exactly as written

example.js4
const orderId = 9007199254740993;
const mask = 0x20000000000001;
const huge = 1e400;
const tiny = 1e-400;

no-magic-numbers

Require numbers to be given a name instead of being used directly

example.js2
function priceWithTax(price) {
  return price * 1.2;
}

setInterval(refresh, 60000);

no-misleading-character-classon by defaulthas suggestions

Disallow characters made of several code points inside a regular expression character class

example.js1
const isThumbsUp = /^[👍]$/;

no-mixed-requiresdeprecated

Disallow require calls mixed with other variables in one declaration

example.js1
const fs = require("fs"),
  retries = 3;

no-multi-assign

Disallow chained assignments such as a = b = c

example.js1
const width = height = 100;

no-multi-str

Disallow strings that continue on the next line with a backslash

example.js1
const help = "Usage: deploy <target> \
  --dry-run";

no-native-reassigndeprecated

Disallow assignments to built-in and other read-only global variables

example.js1
Promise = require("bluebird");

no-negated-condition

Disallow negated conditions in if/else and in ternaries

example.js1
if (!user) {
  redirect("/login");
} else {
  render(user);
}

no-negated-in-lhsdeprecated

Disallow negating the left operand of in

example.js1
if (!key in cache) {
  cache[key] = load(key);
}

no-nested-ternary

Disallow a ternary inside the branch of another ternary

example.js1
const size = width > 1200 ? "large" : width > 600 ? "medium" : "small";

no-new

Disallow new when the created object is thrown away

example.js1
new Router(routes);

no-new-func

Disallow creating functions from strings with Function

example.js1
const add = new Function("a", "b", "return a + b");

no-new-objectdeprecated

Disallow new Object() in favor of {}

example.js1
const headers = new Object();
headers.accept = "text/html";

no-new-requiredeprecated

Disallow new require(..)

example.js1
const server = new require("./server");

no-new-symboldeprecated

Disallow new Symbol()

example.js1
const id = new Symbol("id");

no-new-wrappers

Disallow new String(), new Number() and new Boolean()

example.js1
const enabled = new Boolean(false);

if (enabled) {
  start();
}

no-nonoctal-decimal-escapeon by defaulthas suggestions

Disallow the \8 and \9 escapes in strings

release.cjs1
const version = "v\8.0";

no-obj-callson by default

Disallow calling Math, JSON, Reflect, Atomics, Intl and Temporal as functions

example.js1
const random = Math();

no-object-constructorhas suggestions

Disallow Object() and new Object() without an argument

example.js2
const cache = new Object();
const totals = Object();

no-octalon by default

Disallow number literals that start with 0 followed by a digit

permissions.cjs2
const mode = 0755;
const month = 09;

no-octal-escape

Disallow octal escape sequences such as \251 in strings

notice.cjs1
const notice = "Copyright \251 2026";

no-param-reassign

Disallow assigning to function parameters

example.js2
function total(price, count) {
  price = price * 1.2;
  count++;
  return price * count;
}

no-path-concatdeprecated

Disallow building paths with + from __dirname or __filename

example.js1
const configPath = __dirname + "/config.json";

no-plusplus

Disallow the ++ and -- operators

example.js2
let retries = 0;
retries++;

const next = queue[--position];

no-process-envdeprecated

Disallow process.env

example.js1
function connect() {
  return open(process.env.DATABASE_URL);
}

no-process-exitdeprecated

Disallow calls to process.exit()

example.js1
function loadConfig(path) {
  if (!exists(path)) {
    console.error("No config file");
    process.exit(1);
  }
  return read(path);
}

no-promise-executor-returnhas suggestions

Disallow returning a value from the function passed to new Promise

example.js2
function readCached(key) {
  return new Promise((resolve, reject) => {
    if (cache.has(key)) {
      return cache.get(key);
    }
    load(key, resolve, reject);
  });
}

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

no-proto

Disallow the __proto__ property

example.js2
const parent = user.__proto__;
user["__proto__"] = admin;

no-prototype-builtinson by defaulthas suggestions

Disallow calling hasOwnProperty and other Object.prototype methods on the object itself

example.js2
const data = JSON.parse(body);

if (data.hasOwnProperty("id")) {
  update(data.id);
}

const isChild = base.isPrototypeOf(data);

no-redeclareon by default

Disallow declaring the same variable twice in one scope

example.js2
function summarize(items, label) {
  var total = items.length;
  var label = label || "items";

  for (const item of items) {
    var total = item.price;
  }
  return `${total} ${label}`;
}

no-regex-spaceson by defaultfixable

Disallow several spaces in a row in a regular expression

example.js2
const line = /error   code/;
const header = new RegExp("name  value");

no-restricted-exports

Disallow the export names you list, or default exports

example.js1
export function then(callback) {
  callback("ready");
}

no-restricted-globals

Disallow the global variables you list

example.js2
button.addEventListener("click", () => {
  fetch("/track?id=" + event.target.id);
});

no-restricted-imports

Disallow importing the modules, or the names from them, that you list

example.js2
import moment from "moment";
export { merge } from "lodash";

no-restricted-modulesdeprecated

Disallow loading the modules you list with require()

example.js2
const fs = require("fs");
const request = require("request");

no-restricted-properties

Disallow the object properties you list

example.js3
const area = Math.pow(side, 2);
const volume = Math["pow"](side, 3);
const { pow } = Math;

no-restricted-syntax

Disallow the syntax you describe with selectors

example.js1
for (const key in settings) {
  apply(key, settings[key]);
}

no-return-assign

Disallow assignments in return statements

example.js2
function setName(user, name) {
  return user.name = name;
}

const select = (item) => selected = item;

no-return-awaithas suggestionsdeprecated

Disallow return await where the await changes nothing for the caller

example.js2
async function getUser(id) {
  return await db.find(id);
}

const getItems = async () => await db.all();

no-script-url

Disallow javascript: URLs

example.js2
link.href = "javascript:void(0)";
location.href = `JavaScript:save()`;

no-self-assignon by default

Disallow assigning a variable or a property to itself

example.js3
function rename(user, name) {
  name = name;
  user.name = user.name;
  [user, name] = [user, "guest"];
}

no-self-compare

Disallow comparing a value with itself

example.js1
function isSameUser(user, other) {
  return user.id === user.id;
}

no-sequences

Disallow the comma operator

example.js2
count++, save();

while (item = queue.shift(), item) {
  handle(item);
}

no-setter-returnon by default

Disallow returning a value from a setter

example.js2
class User {
  set name(value) {
    this._name = value;
    return this;
  }
}

Object.defineProperty(config, "port", {
  set(value) {
    return Number(value);
  },
});

no-shadow

Disallow a variable with the same name as one in an outer scope

example.js1
const user = currentUser();

function greet(user) {
  return `Hello, ${user.name}`;
}

no-shadow-restricted-nameson by default

Disallow variables named undefined, NaN, Infinity, eval, arguments or globalThis

example.js2
const undefined = "none";

function toNumber(text, NaN) {
  return text === "" ? NaN : Number(text);
}

no-sparse-arrayson by default

Disallow array literals with holes

example.js1
const colors = ["red",, "blue"];

no-syncdeprecated

Disallow methods with a name that ends in Sync

example.js1
import fs from "node:fs";

function handle(request) {
  const page = fs.readFileSync("index.html", "utf8");
  return new Response(page);
}

no-template-curly-in-string

Disallow ${} placeholders in strings with ordinary quotes

example.js1
const greeting = "Hello, ${user.name}!";

no-ternary

Disallow the ? : operator

example.js1
const label = count === 1 ? "item" : "items";

no-this-before-superon by default

Disallow this and super.x before super() in a constructor

example.js2
class Admin extends User {
  constructor(name) {
    this.role = "admin";
    super.validate(name);
    super(name);
  }
}

no-throw-literal

Require throw to be given something that can be an Error

example.js4
function find(id) {
  if (!id) throw "id is required";
  if (id < 0) throw { code: 400 };
  if (id > max) throw "no such id: " + id;
  throw undefined;
}

no-unassigned-varson by default

Disallow let and var variables that are read but never assigned

example.js1
let status;

if (status === "ready") {
  start();
}

no-undefon by default

Disallow variables that are not declared anywhere

example.js1
function total(items) {
  return items.reduce((sum, item) => sum + item.price, 0) * taxRate;
}

no-undef-initfixable

Disallow initializing a variable to undefined

example.js1
let selected = undefined;

no-undefined

Disallow the use of undefined as a variable

example.js2
let selected = undefined;

if (selected === undefined) {
  selected = first();
}

no-underscore-dangle

Disallow names that start or end with an underscore

example.js3
const _cache = new Map();

function load_(id) {
  return user._id === id;
}

no-unexpected-multilineon by default

Disallow lines that look like a new statement but continue the previous one

example.js1
const name = getName()
[first, last].forEach(print)

no-unmodified-loop-condition

Disallow loop conditions that test variables the loop never changes

example.js1
let node = list.head;

while (node) {
  print(node.value);
  list.next();
}

no-unneeded-ternaryfixable

Disallow ? : where the condition alone says the same

example.js2
const isAdult = age >= 18 ? true : false;
const isGuest = user ? false : true;

no-unreachableon by default

Disallow code after return, throw, break, and continue that can never run

example.js1
function price(item) {
  return item.price * item.quantity;
  console.log("computed", item.id);
}

no-unreachable-loop

Disallow loops with a body that can only run once

example.js1
function firstError(results) {
  for (const result of results) {
    if (result.ok) {
      return null;
    } else {
      return result.error;
    }
  }
}

no-unsafe-finallyon by default

Disallow return, throw, break, and continue in finally blocks

example.js1
function load() {
  try {
    return readConfig();
  } finally {
    return {};
  }
}

no-unsafe-negationon by defaulthas suggestions

Disallow negating the left operand of in and instanceof

example.js2
if (!key in cache) {
  cache[key] = load(key);
}

if (!error instanceof TypeError) {
  throw error;
}

no-unsafe-optional-chainingon by default

Disallow optional chaining where undefined throws an error

example.js4
const { name } = user?.profile;

for (const item of order?.items) {
  ship(item);
}

const all = [...order?.items];

const data = (await response?.json()).data;

no-unused-expressions

Disallow statements that compute a value and throw it away

example.js3
server.close;
count === 0;
`Hello, ${name}`;

no-unused-labelson by defaultfixable

Disallow labels that no break or continue refers to

example.js1
rows: for (const row of table) {
  for (const cell of row) {
    if (cell.isEmpty) break;
  }
}

no-unused-private-class-memberson by defaulthas suggestions

Disallow #private class members that are never used

example.js3
class Counter {
  #count = 0;
  #step = 1;

  #log() {
    console.log("changed");
  }

  increment() {
    this.#count++;
  }
}

no-unused-varson by defaulthas suggestions

Disallow variables, functions, imports, and parameters that are never used

example.js4
import { readFile, writeFile } from "node:fs/promises";

let saved = 0;

async function save(path, data, options) {
  await writeFile(path, data);
  saved++;
}

no-use-before-define

Disallow using a variable, function, or class above the line that declares it

example.js3
console.log(limit);
const limit = 10;

start();
function start() {}

const total = total + 1;

no-useless-assignmenton by default

Disallow assigning a value to a variable that is never read afterwards

example.js1
function label(user) {
  let name = "guest";
  name = user.name;
  return name;
}

no-useless-backreferenceon by default

Disallow regular expression backreferences that always match the empty string

example.js4
const quoted = /\1.*?(["'])/;
const either = /(a)|\1b/;
const doubled = new RegExp("(\\w\\1)");
const notQuoted = /(?!(["']))\w+\1/;

no-useless-call

Disallow .call() and .apply() where a normal call does the same

example.js3
format.call(undefined, value, locale);
format.apply(null, [value, locale]);
logger.write.call(logger, message);

no-useless-catchon by default

Disallow catch clauses that only rethrow the error

example.js1
try {
  await save(user);
} catch (error) {
  throw error;
}

no-useless-computed-keyfixable

Disallow brackets around a property key that is a plain string or number

example.js4
const headers = { ["content-type"]: "text/plain", [0]: "first" };

const { ["content-type"]: type } = headers;

class Cache {
  ["clear"]() {}
}

no-useless-concat

Disallow joining two string literals with + on the same line

example.js2
const greeting = "Hello, " + "world";
const path = base + "/api" + `/users`;

no-useless-constructorhas suggestions

Disallow constructors that do what the default constructor does

example.js2
class Store {
  constructor() {}
}

class UserStore extends Store {
  constructor(name, options) {
    super(name, options);
  }
}

no-useless-escapeon by defaulthas suggestions

Disallow backslashes that do not change the meaning of a string or regular expression

example.js5
const quote = "It\'s here";
const digits = new RegExp("\d+");
const price = `\#${amount}`;
const email = /[\w\.]+\@example\.com/;

no-useless-renamefixable

Disallow renaming an import, export, or destructured value to the same name

example.js3
import { load as load } from "./store.js";

const { id: id } = load();

export { id as id };

no-useless-returnfixable

Disallow a return without a value where the function ends anyway

example.js2
function reset(form) {
  form.clear();
  return;
}

function notify(user) {
  if (user.muted) {
    return;
  } else {
    send(user);
  }
}

no-varon by defaultfixable

Require let or const instead of var

example.js1
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i)); // 3, 3, 3
}

no-void

Disallow the void operator

example.js2
let selected = void 0;

button.onclick = () => void save();

no-warning-comments

Disallow comments that start with TODO, FIXME, or another term you choose

example.js2
// TODO: handle an empty list
function total(items) {
  /* fixme: rounding */
  return items.length * 1.1;
}

no-withon by default

Disallow with statements

legacy.cjs1
with (point) {
  distance = Math.sqrt(x * x + y * y);
}

object-shorthandfixable

Require or disallow the short forms { name } and { save() {} } in object literals

example.js2
const user = {
  name: name,
  save: function () {
    store(this);
  },
};

one-varfixable

Require variables to be declared in one statement, or in one statement each

example.js1
function area(shape) {
  const width = shape.width;
  const height = shape.height;
  return width * height;
}

operator-assignmentfixable

Require or disallow shorthand assignment operators such as +=

example.js3
total = total + price;
user.visits = user.visits + 1;
area = width * area;

prefer-arrow-callbackfixable

Require arrow functions for callbacks

example.js2
const prices = items.map(function (item) {
  return item.price;
});

button.addEventListener("click", function () {
  this.save();
}.bind(this));

prefer-conston by defaultfixable

Require const for variables that are never reassigned

example.js3
let total = items.length;
console.log(total);

for (let item of items) {
  console.log(item);
}

let label;
label = "Total";

prefer-destructuringfixable

Require destructuring to read a property or an element into a variable

example.js3
const name = user.name;
const first = items[0];
email = user.email;

prefer-exponentiation-operatorfixable

Require the ** operator instead of Math.pow()

example.js3
const area = Math.PI * Math.pow(radius, 2);
const distance = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));

prefer-named-capture-grouphas suggestions

Require capture groups in regular expressions to have a name

example.js3
const date = /(\d{4})-(\d{2})/.exec(input);
const year = date[1];

const version = new RegExp("v([0-9]+)");

prefer-numeric-literalsfixable

Require binary, octal and hexadecimal literals instead of parseInt() on a fixed string

example.js3
const mask = parseInt("1010", 2);
const mode = Number.parseInt("644", 8);
const color = parseInt("ff8800", 16);

prefer-object-has-ownfixable

Require Object.hasOwn() instead of Object.prototype.hasOwnProperty.call()

example.js3
if (Object.prototype.hasOwnProperty.call(user, "email")) {
  send(user.email);
}

const isSet = {}.hasOwnProperty.call(settings, key);
const isCached = Object.hasOwnProperty.call(cache, id);

prefer-object-spreadfixable

Require object spread instead of Object.assign() into a new object

example.js3
const settings = Object.assign({}, defaults, options);
const admin = Object.assign({ role: "admin" }, user);
const point = Object.assign({ x: 0, y: 0 });

prefer-promise-reject-errors

Require an Error object as the reason of a promise rejection

example.js2
Promise.reject("User not found");

new Promise((resolve, reject) => {
  reject({ code: 404 });
});

prefer-reflectdeprecated

Require Reflect methods instead of their older Object and Function forms

example.js4
save.apply(user, args);
save.call(user, "draft");
Object.defineProperty(user, "id", { value: 1 });
delete user.email;

prefer-regex-literalshas suggestions

Require regular expression literals instead of RegExp() with a fixed pattern

example.js3
const digits = new RegExp("\\d+");
const word = RegExp("hello", "gi");
const path = new RegExp(String.raw`\.tsx?$`);

prefer-rest-paramson by default

Require rest parameters instead of arguments

example.js1
function log(level) {
  const parts = Array.prototype.slice.call(arguments, 1);
  console.log(level, parts.join(" "));
}

prefer-spreadon by default

Require spread syntax instead of .apply() to pass an array as arguments

example.js3
const highest = Math.max.apply(Math, values);
notify.apply(null, listeners);
logger.info.apply(logger, parts);

prefer-templatefixable

Require template literals instead of string concatenation

example.js2
const greeting = "Hello, " + user.name + "!";
const url = base + "/users/" + id;

preserve-caught-erroron by defaulthas suggestions

Require the caught error as the cause of a new error thrown in catch

example.js1
try {
  loadConfig();
} catch (error) {
  throw new Error("Could not load the config");
}

radixhas suggestions

Require the radix argument of parseInt()

example.js4
const page = parseInt(query.page);
const size = Number.parseInt(query.size);
const level = parseInt(input, 1);
const depth = parseInt(input, "10");

require-atomic-updates

Disallow assignments that use a value read before an await or a yield

example.js1
let total = 0;

async function addPrice(id) {
  total += await fetchPrice(id);
}

require-awaithas suggestions

Disallow async functions that never use await

example.js1
async function getUser(id) {
  return cache.get(id);
}

require-unicode-regexphas suggestions

Require the u or v flag on regular expressions

example.js2
const oneCharacter = /^.$/;
const word = new RegExp("\\w+", "g");

require-yieldon by default

Require generator functions to contain yield

example.js1
function* ids() {
  return nextId();
}

sort-importsfixable

Enforce sorted import declarations and sorted names inside them

example.js1
import { writeFile, readFile } from "node:fs/promises";

sort-keys

Require the keys of object literals to be sorted

example.js1
const user = {
  name: "Ada",
  email: "ada@example.com",
  role: "admin",
};

sort-varsfixable

Require the variables of one declaration to be sorted

example.js2
let width, height, depth;

strictfixable

Require or disallow "use strict" directives

example.js1
"use strict";

export function run() {
  save();
}

symbol-description

Require a description when creating a Symbol

example.js1
const userId = Symbol();

unicode-bomfixable

Require or disallow a Unicode byte order mark at the start of a file

example.js1
const port = 3000;

use-isnanon by defaulthas suggestions

Require Number.isNaN() instead of a comparison with NaN

example.js1
const price = Number(input);

if (price === NaN) {
  showError();
}

valid-typeofon by defaulthas suggestions

Require typeof to be compared with a valid type name

example.js3
if (typeof name === "strnig") {
  trim(name);
}

if (typeof callback === undefined) {
  callback = noop;
}

if (typeof user === null) {
  signIn();
}

vars-on-top

Require var declarations at the top of their function or file

example.js2
function total(items) {
  var sum = 0;
  for (var i = 0; i < items.length; i++) {
    var price = items[i].price;
    sum += price;
  }
  return sum;
}

yodafixable

Require or disallow comparisons with the literal on the left

example.js2
if ("red" === color) {
  stop();
}

if (0 < count) {
  render();
}

typescript-eslint 137 rules

@typescript-eslint/array-typefixable

Require either T[] or Array<T> for array types, consistently

example.ts2
const names: Array<string> = [];
const ids: ReadonlyArray<number> = [1, 2];

@typescript-eslint/await-thenablehas suggestionsneeds types

Disallow await on a value that is not a promise

example.ts1
function readConfig(): string {
  return "{}";
}

const config = await readConfig();

@typescript-eslint/ban-ts-commenton by defaulthas suggestions

Disallow @ts-ignore and @ts-nocheck, and require a reason after @ts-expect-error

example.ts1
// @ts-ignore
const port: number = process.env.PORT;

@typescript-eslint/ban-tslint-commentfixable

Disallow // tslint:disable and // tslint:enable comments

example.ts2
/* tslint:disable */
const port = 3000;
console.log(port); // tslint:disable-line:no-console

@typescript-eslint/ban-types

Disallow String, Number, Object, Function, {} and similar types

example.ts2
function greet(name: String, onDone: Function) {
  onDone(`Hello, ${name}`);
}

@typescript-eslint/class-methods-use-this

Require class methods to use this

example.ts2
class Invoice {
  total = 0;

  format(amount: number) {
    return amount.toFixed(2);
  }

  round = (amount: number) => Math.round(amount);
}

@typescript-eslint/consistent-generic-constructorsfixable

Require type arguments on the constructor call or on the type annotation, consistently

example.ts3
const scores: Map<string, number> = new Map();

class Game {
  players: Set<string> = new Set();
}

function start(seen: Set<string> = new Set()) {}

@typescript-eslint/consistent-returnneeds types

Require every return in a function to return a value, or none of them

example.ts1
function findUser(id: number) {
  if (id < 0) {
    return;
  }
  return { id };
}

@typescript-eslint/consistent-type-importsfixable

Require import type for imports that are only used as types

example.ts1
import { User } from "./models";

export function greet(user: User) {
  return `Hello, ${user.name}`;
}

@typescript-eslint/default-param-last

Require parameters with a default value, and optional parameters, to come last

example.ts1
function connect(port = 3000, host: string) {}

connect(undefined, "localhost");

@typescript-eslint/dot-notationfixableneeds types

Require user.name instead of user["name"]

example.ts1
const user = { name: "Ada" };

console.log(user["name"]);

@typescript-eslint/explicit-function-return-type

Require a return type on functions and class methods

example.ts3
function total(prices: number[]) {
  return prices.reduce((sum, price) => sum + price, 0);
}

const isEmpty = (items: string[]) => items.length === 0;

class Cart {
  size() {
    return 0;
  }
}

@typescript-eslint/explicit-module-boundary-types

Require parameter and return types on exported functions and the methods of exported classes

example.ts3
export function parsePrice(text) {
  return Number(text.replace("$", ""));
}

export function log(value: any): void {
  console.log(value);
}

@typescript-eslint/max-params

Enforce a maximum number of parameters in function definitions

example.ts1
function createUser(name: string, email: string, isAdmin: boolean, isActive: boolean) {
  return { name, email, isAdmin, isActive };
}

@typescript-eslint/member-ordering

Require a consistent order for the members of classes, interfaces and type literals

example.ts2
class Timer {
  start(): void {}

  constructor() {}

  private elapsed = 0;
}

@typescript-eslint/naming-convention

Enforce naming conventions for variables, functions, types, members and imports

example.ts3
const user_name = "ada";

function Load_user() {}

interface user_record {}

@typescript-eslint/no-array-deletehas suggestionsneeds types

Disallow delete on an element of an array

example.ts1
const items = ["a", "b", "c"];

delete items[1]; // ["a", <empty>, "c"], length 3

@typescript-eslint/no-base-to-stringneeds types

Disallow turning an object into a string when the result is [object Object]

example.ts3
const user = { name: "Ada" };

console.log(`Hello, ${user}`);
console.log("Hello, " + user);
console.log([user].join(", "));

@typescript-eslint/no-confusing-non-null-assertionhas suggestions

Disallow a non-null assertion ! right before =, ==, ===, in or instanceof

example.ts3
declare let user: string | undefined;
declare const cache: Record<string, number>;

const isSame = user! == "ada";
user! = "grace";
const isCached = user! in cache;

@typescript-eslint/no-confusing-void-expressionfixableneeds types

Require expressions of type void to stand alone as statements

example.ts3
declare function log(message: string): void;

const result = log("saved");

const onSave = () => log("saved");

function onError(error: Error) {
  return log(error.message);
}

@typescript-eslint/no-deprecatedneeds types

Disallow using code marked as @deprecated

example.ts2
/** @deprecated Use `fetchUser` instead. */
function getUser(id: number) {}

interface Options {
  /** @deprecated */
  legacy?: boolean;
}

getUser(1);

function start(options: Options) {
  return options.legacy;
}

@typescript-eslint/no-duplicate-type-constituentsfixableneeds types

Disallow the same type twice in a union or intersection

example.ts4
type Id = string | number | string;

type Name = string;
type Label = Name | string;

type Point = { x: number } & { x: number };

function greet(name?: string | undefined) {}

@typescript-eslint/no-dynamic-delete

Disallow delete with a computed property key

example.ts1
const sessions: Record<string, Date> = {};

function logout(userId: string) {
  delete sessions[userId];
}

@typescript-eslint/no-empty-interfacefixabledeprecated

Disallow interfaces that declare no members

example.ts2
interface Options {}

interface User {
  name: string;
}

interface Admin extends User {}

@typescript-eslint/no-explicit-anyon by defaultfixable

Disallow the any type

example.ts1
function parse(text: string): any {
  return JSON.parse(text);
}

parse("{}").user.nmae; // no type error

@typescript-eslint/no-extra-non-null-assertionon by defaultfixable

Disallow a non-null assertion that has no effect

example.ts2
function nameOf(user?: { name: string }) {
  return user!!.name;
}

function lengthOf(items?: string[]) {
  return items!?.length;
}

@typescript-eslint/no-extraneous-class

Disallow classes that are empty, only static, or only a constructor

example.ts1
export class MathUtils {
  static readonly TAU = Math.PI * 2;

  static clamp(value: number, min: number, max: number) {
    return Math.min(Math.max(value, min), max);
  }
}

@typescript-eslint/no-floating-promiseshas suggestionsneeds types

Require promises to be awaited, returned, or handled

example.ts1
async function save(data: string): Promise<void> {}

function onClick() {
  save("draft");
}

@typescript-eslint/no-for-in-arrayneeds types

Disallow for...in over an array

example.ts1
const prices = [5, 10, 20];

for (const i in prices) {
  console.log(i + 1); // "01", "11", "21"
}

@typescript-eslint/no-implied-evalneeds types

Disallow passing code as a string to setTimeout, setInterval and new Function

example.ts3
setTimeout("refresh()", 1000);

function schedule(code: string) {
  globalThis.setInterval(code, 1000);
}

const add = new Function("a", "b", "return a + b");

@typescript-eslint/no-inferrable-typesfixable

Disallow type annotations that repeat what the initial value already says

example.ts4
const retries: number = 3;
const pattern: RegExp = /\d+/;

function greet(name: string = "world") {}

class Cart {
  isOpen: boolean = false;
}

@typescript-eslint/no-invalid-this

Disallow this where its value is undefined

example.ts1
const button = {
  label: "Save",
  onClick() {
    setTimeout(function () {
      console.log(this.label);
    }, 100);
  },
};

@typescript-eslint/no-invalid-void-type

Disallow void outside of return types and generic type arguments

example.ts3
let result: void;

function log(message: string | void) {}

type Listener = (event: void) => void;

@typescript-eslint/no-loop-funcdeprecated

Disallow functions in loops that use a variable the loop changes

example.ts1
const handlers: Array<() => void> = [];

for (var i = 0; i < 3; i++) {
  handlers.push(() => console.log(i)); // 3, 3, 3
}

@typescript-eslint/no-loss-of-precisiondeprecated

Disallow number literals that cannot be stored exactly as written

example.ts2
const orderId = 9007199254740993; // stored as 9007199254740992
const limit = 2e999; // stored as Infinity

@typescript-eslint/no-meaningless-void-operatorfixableneeds types

Disallow void in front of an expression that has no value to discard

example.ts2
function log(message: string): void {
  console.log(message);
}

void log("saved");

const count = 3;
void count;

@typescript-eslint/no-misused-newon by default

Disallow new in a class and constructor in an interface

example.ts3
declare class Client {
  new(): Client;
}

interface Session {
  new (): Session;
  constructor(): void;
}

@typescript-eslint/no-misused-promisesneeds types

Disallow promises where the code around them cannot handle one

example.ts3
declare function isAdmin(id: string): Promise<boolean>;
declare function remove(id: string): Promise<void>;
declare function load(id: string): Promise<{ name: string }>;

function cleanUp(ids: string[]) {
  if (isAdmin(ids[0])) {
    ids.forEach(async id => {
      await remove(id);
    });
  }
  return { id: ids[0], ...load(ids[0]) };
}

@typescript-eslint/no-misused-spreadhas suggestionsneeds types

Disallow ... on values that do not spread the way they look

example.ts3
declare function loadUser(): Promise<{ name: string }>;
const roles = new Map([["admin", true]]);
const tags = ["new", "sale"];

const user = { ...loadUser() };
const access = { ...roles };
const labels = { ...tags };

@typescript-eslint/no-namespaceon by default

Disallow TypeScript namespace and module blocks

example.ts1
namespace Format {
  export function price(cents: number) {
    return `$${cents / 100}`;
  }
}

Format.price(1999);

@typescript-eslint/no-non-null-asserted-nullish-coalescinghas suggestions

Disallow ! on the left side of ??

example.ts2
declare const settings: { theme?: string };
declare function findUser(id: string): { name: string } | null;

const theme = settings.theme! ?? "light";
const user = findUser("42")! ?? { name: "guest" };

@typescript-eslint/no-non-null-asserted-optional-chainon by defaulthas suggestions

Disallow ! after an optional chain

example.ts2
declare const user: { name: string; address?: { city: string } } | undefined;

const name: string = user?.name!;
const city: string = (user?.address?.city)!;

@typescript-eslint/no-non-null-assertionhas suggestions

Disallow the non-null assertion value!

example.ts2
const users = new Map<string, { name: string }>();

function greet(id: string) {
  const form = document.querySelector("form")!;
  form.title = users.get(id)!.name;
}

@typescript-eslint/no-redeclare

Disallow declaring the same name twice in one scope

example.ts1
function price(items: number[]) {
  var total = items.length;
  for (const item of items) {
    var total = item;
  }
  return total;
}

@typescript-eslint/no-redundant-type-constituentsneeds types

Disallow members of unions and intersections that change nothing or swallow the rest

example.ts5
type Status = "draft" | "published" | string;

type Payload = { id: number } | any;

type Result = string | never;

function send(body: unknown | string) {}

@typescript-eslint/no-require-imports

Disallow require() in favor of import

example.ts2
const path = require("node:path");
import fs = require("node:fs");

console.log(path.join("a", "b"), fs.existsSync("a"));

@typescript-eslint/no-restricted-importsdeprecated

Disallow imports of the modules you list, with an exception for type imports

example.ts3
import moment from "moment";
import legacy = require("moment");
export { duration } from "moment";

@typescript-eslint/no-shadow

Disallow a declaration that hides one with the same name in an outer scope

example.ts1
const timeout = 1000;

function retry(action: () => void) {
  const timeout = 50;
  setTimeout(action, timeout);
}

@typescript-eslint/no-this-aliason by default

Disallow storing this in a variable

example.ts1
class Timer {
  seconds = 0;

  start() {
    const self = this;
    setInterval(function () {
      self.seconds++;
    }, 1000);
  }
}

@typescript-eslint/no-type-aliasdeprecated

Disallow type aliases, or allow only some kinds of them

example.ts3
type UserId = string;

type User = {
  id: string;
};

type OnSave = (user: User) => void;

@typescript-eslint/no-unnecessary-conditionhas suggestionsneeds types

Disallow conditions that the types say are always true or always false

example.ts3
interface User {
  name: string;
  tags: string[];
}

function label(user: User) {
  if (user.tags) {
    console.log(user.tags.length);
  }
  const name = user.name ?? "anonymous";
  return user?.name + name;
}

@typescript-eslint/no-unnecessary-qualifierfixableneeds types

Disallow naming the enclosing namespace or enum where the short name works

example.ts2
namespace Shapes {
  export interface Circle {
    radius: number;
  }
  export const unit: Shapes.Circle = { radius: 1 };
}

enum Level {
  Low = 1,
  Default = Level.Low,
}

@typescript-eslint/no-unnecessary-template-expressionfixableneeds types

Disallow ${} in a template literal where plain text or the value itself does the same

example.ts4
function describe(name: string, width: number) {
  const title = `${name}`;
  const size = `${width}${"px"}`;
  const retries = `retries: ${3}`;
  return [title, size, retries];
}

type Size = `${number}${"px"}`;

@typescript-eslint/no-unnecessary-type-argumentsfixableneeds types

Disallow a type argument that is the same as the default of its type parameter

example.ts3
class Store<Value = string> {
  items: Value[] = [];
}

const labels: Store<string> = new Store<string>();

class TagStore extends Store<string> {}

@typescript-eslint/no-unnecessary-type-assertionfixableneeds types

Disallow as, <T> and ! assertions that do not change the type

example.ts3
interface User {
  name: string;
  email?: string;
}

function show(user: User) {
  const name = user.name as string;
  const length = user.name!.length;
  const email: string | undefined = user.email!;
  return [name, length, email];
}

@typescript-eslint/no-unnecessary-type-constrainton by defaulthas suggestions

Disallow extends any and extends unknown on a type parameter

example.ts3
function first<Item extends unknown>(items: Item[]) {
  return items[0];
}

class Box<Value extends any> {
  constructor(public value: Value) {}
}

type Pair<Left, Right extends unknown> = [Left, Right];

@typescript-eslint/no-unnecessary-type-conversionhas suggestionsneeds types

Disallow converting a value to the type it already has

example.ts4
function format(name: string, count: number, isOpen: boolean) {
  const label = String(name);
  const title = name.toString() + ":";
  const total = +count;
  const open = !!isOpen;
  return [label, title, total, open];
}

@typescript-eslint/no-unnecessary-type-parametershas suggestionsneeds types

Disallow a type parameter that is used only once in a signature

example.ts3
function log<Message>(message: Message): void {
  console.log(message);
}

function size<Item extends { length: number }>(item: Item): number {
  return item.length;
}

function load<Data>(key: string): Data {
  return JSON.parse(localStorage.getItem(key) ?? "null");
}

@typescript-eslint/no-unsafe-argumentneeds types

Disallow passing a value of type any as an argument

example.ts3
declare function resize(width: number, height: number): void;
declare function addTags(tags: Set<string>): void;

const config = JSON.parse("{}");

resize(config.width, 100);
resize(...config.size);
addTags(new Set<any>());

@typescript-eslint/no-unsafe-assignmentneeds types

Disallow assigning a value of type any to a variable or a property

example.ts4
const config = JSON.parse("{}");
const port: number = JSON.parse("8080");
const { host } = JSON.parse("{}") as { host: any };
const tags: Set<string> = new Set<any>();

@typescript-eslint/no-unsafe-callneeds types

Disallow calling a value of type any

example.ts4
function run(plugin: any, handler: Function) {
  plugin();
  plugin.setup();
  new plugin.Worker();
  handler();
}

@typescript-eslint/no-unsafe-declaration-mergingon by default

Disallow a class and an interface with the same name

example.ts2
interface User {
  name: string;
}

class User {}

// No compile error, but it throws when it runs.
console.log(new User().name.length);

@typescript-eslint/no-unsafe-enum-assignmentneeds types

Disallow giving a plain number or string to a place typed as an enum

example.ts6
enum Status {
  Open,
  Closed,
}

declare function setStatus(next: Status): void;
declare const labels: { [key in Status]: string };

let current: Status = 1;
current++;
setStatus(0);
console.log(labels[0]);
const parsed = Number("1") as Status;
const all: Status[] = [Status.Open, 1];

@typescript-eslint/no-unsafe-enum-comparisonhas suggestionsneeds types

Disallow comparing an enum value with a plain number or string

example.ts3
enum Status {
  Open,
  Closed,
}

enum Color {
  Red = "red",
}

function describe(status: Status, color: Color) {
  if (status === 1) {
    return "closed";
  }
  if (color === "red") {
    return "red";
  }
  switch (status) {
    case 0:
      return "open";
  }
}

@typescript-eslint/no-unused-expressionson by default

Disallow statements that compute a value and do nothing with it

example.ts4
declare const user: { name?: string; save(): void };

user.save;
user.name === "admin";
user?.name;
user.name!;

@typescript-eslint/no-unused-varson by defaultfixable

Disallow variables, functions, parameters, imports, and types that are never used

example.ts4
import { readFile } from "node:fs/promises";

interface Options {
  retries: number;
}

let attempts = 0;
attempts = attempts + 1;

function retry() {}

@typescript-eslint/no-use-before-define

Disallow using a variable, function, class, enum, or type above the line that declares it

example.ts3
console.log(limit);
const limit = 10;

start(Mode.Fast);
function start(mode: Mode) {}

enum Mode {
  Fast,
  Slow,
}

@typescript-eslint/no-useless-constructorhas suggestions

Disallow constructors that do what the default constructor does

example.ts2
class Store {
  constructor() {}
}

class HttpError extends Error {
  constructor(message: string) {
    super(message);
  }
}

@typescript-eslint/no-var-requiresdeprecated

Disallow const x = require() in favor of import

example.ts3
const fs = require("node:fs");
const join = require("node:path").join;
register(require("./plugin"));

@typescript-eslint/no-wrapper-object-typeson by defaultfixable

Disallow String, Number, Boolean, BigInt, Symbol, and Object as types

example.ts3
function repeat(text: String, times: Number): String {
  return text.repeat(times.valueOf());
}

@typescript-eslint/only-throw-errorneeds types

Disallow throwing values that are not Error objects

example.ts3
function load(path: string, status: number) {
  if (!path) throw "path is required";
  if (status === 404) throw { status };
  throw undefined;
}

@typescript-eslint/prefer-as-conston by defaultfixable

Require as const instead of repeating a literal as its own type

example.ts3
let theme = "dark" as "dark";
let retries: 3 = 3;

class Request {
  method: "GET" = "GET";
}

@typescript-eslint/prefer-destructuringfixableneeds types

Require destructuring to read a property or an element into a variable

example.ts3
declare const user: { name: string; email: string };
declare const items: string[];
let email: string;

const name = user.name;
const first = items[0];
email = user.email;

@typescript-eslint/prefer-findhas suggestionsneeds types

Require .find() instead of .filter()[0] to get the first match

example.ts2
declare const users: { name: string; admin: boolean }[];

const admin = users.filter(user => user.admin)[0];
const guest = users.filter(user => !user.admin).at(0);

@typescript-eslint/prefer-for-of

Require for...of instead of a for loop that only uses its index to read the array

example.ts1
for (let i = 0; i < items.length; i++) {
  console.log(items[i]);
}

@typescript-eslint/prefer-function-typefixable

Require a function type instead of an interface or object type with only a call signature

example.ts2
interface Handler {
  (event: Event): void;
}

function listen(callback: { (event: Event): void }) {}

@typescript-eslint/prefer-includesfixableneeds types

Require .includes() instead of comparing .indexOf() with -1

example.ts3
function check(tags: string[], path: string) {
  if (tags.indexOf("draft") !== -1) return false;
  if (path.indexOf("/private/") >= 0) return false;
  return /admin/.test(path);
}

@typescript-eslint/prefer-namespace-keywordon by defaultfixable

Require namespace instead of module to declare a namespace

example.ts2
module Shapes {
  export const sides = 4;
}

declare module Vendor {
  function init(): void;
}

@typescript-eslint/prefer-nullish-coalescinghas suggestionsneeds types

Require ?? instead of || or a ternary to give a default for null or undefined

example.ts4
function connect(options: { port?: number; host: string | null; name?: string; tags?: string[] }) {
  const port = options.port || 3000;
  const host = options.host !== null ? options.host : "localhost";
  options.name ||= "default";
  if (!options.tags) {
    options.tags = [];
  }
}

@typescript-eslint/prefer-optional-chainfixableneeds types

Require a?.b instead of a && a.b, !a || !a.b, or (a || {}).b

example.ts3
type User = { profile?: { city?: string; refresh?: () => void } };

function show(user: User | undefined) {
  const city = user && user.profile && user.profile.city;
  if (!user || !user.profile) return;
  return (user.profile || {}).refresh;
}

@typescript-eslint/prefer-promise-reject-errorsneeds types

Require an Error as the reason when a promise is rejected

example.ts3
function load(url: string, reason: string) {
  if (!url) return Promise.reject("missing url");
  if (url === "/") return Promise.reject();

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

@typescript-eslint/prefer-readonlyfixableneeds types

Require readonly on private members that are only assigned in the constructor

example.ts4
class Cache {
  private entries = new Map<string, string>();
  #limit = 100;
  private name: string;

  constructor(name: string, private ttl: number) {
    this.name = name;
  }

  set(key: string, value: string) {
    this.entries.set(key, value);
  }
}

@typescript-eslint/prefer-readonly-parameter-typesneeds types

Require the types of function parameters to be read-only

example.ts3
function total(prices: number[]) {}

function rename(user: { name: string }) {}

function names(users: readonly { name: string }[]) {}

@typescript-eslint/prefer-reduce-type-parameterfixableneeds types

Require a type argument on reduce instead of a type assertion on the initial value

example.ts2
function count(words: string[]) {
  return words.reduce((counts, word) => {
    counts[word] = (counts[word] ?? 0) + 1;
    return counts;
  }, {} as Record<string, number>);
}

function lengths(words: string[]) {
  return words.reduce((result, word) => result.concat(word.length), [] as number[]);
}

@typescript-eslint/prefer-regexp-execfixableneeds types

Require pattern.exec(text) instead of text.match(pattern) when there is no g flag

example.ts2
function version(text: string) {
  return text.match(/v(\d+)/) ?? text.match("latest");
}

@typescript-eslint/prefer-return-this-typefixableneeds types

Require the return type this for a method that returns this

example.ts2
class Query {
  where(condition: string): Query {
    return this;
  }

  limit = (count: number): Query => this;
}

@typescript-eslint/prefer-string-starts-ends-withfixableneeds types

Require startsWith and endsWith instead of other ways to test the start or end of a string

example.ts4
function isApiRoute(path: string) {
  return path.indexOf("/api") === 0;
}

function isHidden(name: string) {
  return name[0] === ".";
}

function isTypeScript(file: string) {
  return file.slice(-3) === ".ts" || /\.tsx$/.test(file);
}

@typescript-eslint/promise-function-asyncfixableneeds types

Require functions that return a promise to be async

example.ts2
function loadUser(id: string) {
  if (!id) throw new Error("missing id");
  return fetch(`/users/${id}`);
}

const cached = (hit?: string) => hit ?? Promise.resolve("miss");

@typescript-eslint/require-array-sort-compareneeds types

Require a compare function in calls to sort and toSorted on arrays

example.ts2
const prices = [5, 100, 20];

prices.sort(); // [100, 20, 5]
const sorted = prices.toSorted();

@typescript-eslint/require-awaithas suggestionsneeds types

Disallow async functions that never await and do not return a promise

example.ts2
async function total(prices: number[]) {
  return prices.reduce((sum, price) => sum + price, 0);
}

const log = async (message: string) => {
  console.log(message);
};

@typescript-eslint/restrict-plus-operandsneeds types

Require both operands of + to be numbers, bigints, or strings

example.ts3
function describe(user: { name: string }, tags: string[], size: bigint) {
  const label = "User: " + user;
  const list = "Tags: " + tags;
  const next = size + 1;
}

@typescript-eslint/restrict-template-expressionsneeds types

Disallow values in template literals that do not turn into a useful string

example.ts2
async function loadName(): Promise<string> {
  return "Ada";
}

const user = { name: "Ada" };
console.log(`Hello ${user}`); // Hello [object Object]
console.log(`Hello ${loadName()}`); // Hello [object Promise]

@typescript-eslint/return-awaitfixableneeds types

Enforce consistent use of await when an async function returns a promise

example.ts2
async function save(data: string): Promise<void> {}

async function trySave(data: string) {
  try {
    return save(data);
  } catch (error) {
    console.error("not reached when save() rejects", error);
  }
}

async function saveDraft() {
  return await save("draft");
}

@typescript-eslint/sort-type-constituentsfixabledeprecated

Require the members of union and intersection types to be sorted

example.ts3
type Status = "open" | "closed" | "merged";
type Id = null | string | UserId;
type Props = WithStyle & WithChildren;

@typescript-eslint/strict-boolean-expressionshas suggestionsneeds types

Disallow nullable numbers, strings, and booleans, any, and always-true values in conditions

example.ts3
function render(width?: number, title?: string, items: string[] = []) {
  if (width) {
    resize(width); // skipped for 0
  }
  const heading = title || "Untitled"; // replaces ""
  if (items) {
    draw(items); // always runs, even for []
  }
}

declare function resize(width: number): void;
declare function draw(items: string[]): void;

@typescript-eslint/strict-void-returnhas suggestionsneeds types

Disallow passing a function that returns a value where a void function is expected

example.ts3
declare function onChange(listener: (value: string) => void): void;
declare function save(value: string): Promise<void>;
const history: string[] = [];

onChange(async value => {
  await save(value);
});
onChange(value => history.push(value));

const stop: () => void = () => {
  return history.length;
};

@typescript-eslint/switch-exhaustiveness-checkhas suggestionsneeds types

Require a switch on a union or an enum to have a case for every member

example.ts1
type Status = "open" | "closed" | "merged";

function label(status: Status) {
  switch (status) {
    case "open":
      return "Open";
    case "closed":
      return "Closed";
  }
}

@typescript-eslint/triple-slash-referenceon by default

Disallow /// <reference /> comments where an import does the job

example.ts2
/// <reference path="./globals.d.ts" />
/// <reference types="lodash" />
import { chunk } from "lodash";

export const pages = chunk([1, 2, 3, 4], 2);

@typescript-eslint/typedefdeprecated

Require type annotations in the places you choose

example.ts2
function greet(name) {
  return `Hello ${name}`;
}

const double = value => value * 2;

@typescript-eslint/unbound-methodneeds types

Disallow taking a method off its object without binding it

example.ts3
class User {
  name = "Ada";
  greet() {
    return `Hello ${this.name}`;
  }
}

const user = new User();
const greet = user.greet;
const { greet: hello } = user;
setTimeout(user.greet, 100);

@typescript-eslint/unified-signatures

Disallow overloads that can be written as one signature

example.ts2
function find(id: string): void;
function find(id: number): void;
function find(id: string | number) {}

interface Logger {
  log(message: string): void;
  log(message: string, level: number): void;
}

@typescript-eslint/use-unknown-in-catch-callback-variablehas suggestionsneeds types

Require the parameter of a promise rejection callback to be typed unknown

example.ts3
declare function save(): Promise<void>;

save().catch(error => {
  console.error(error.message);
});
save().catch((error: Error) => {
  console.error(error.message);
});
save().then(
  () => console.log("saved"),
  ({ message }) => console.error(message),
);

React 135 rules

react/boolean-prop-naming

Enforce a naming pattern such as isOpen or hasIcon for boolean props

example.tsx1
type Props = { open: boolean; title: string };

function Dialog(props: Props) {
  return <dialog open={props.open}>{props.title}</dialog>;
}

react/button-has-type

Require an explicit, valid type on every button element

example.jsx1
<form>
  <button onClick={openMenu}>Options</button>
</form>;

react-hooks/capitalized-calls

Disallow calling a function with a capitalized name from a component or a hook

example.jsx1
import Avatar from "./Avatar";

function Profile({ user }) {
  return <section>{Avatar({ user })}</section>;
}

react/default-props-match-prop-types

Require every entry of defaultProps to have a prop type that is not required

example.jsx2
function Greeting({ name, greeting }) {
  return <p>{greeting}, {name}</p>;
}
Greeting.propTypes = {
  name: PropTypes.string.isRequired,
  greeting: PropTypes.string,
};
Greeting.defaultProps = {
  name: "friend", // required, so never used
  greting: "Hello", // no such prop
};

react/destructuring-assignmentfixable

Enforce one style for reading props, state, and context: destructured or not

example.jsx2
function Badge(props) {
  return <span className={props.color}>{props.label}</span>;
}

react/display-name

Require every component to have a name that React can show

example.jsx1
export default () => <nav>Menu</nav>;

react-hooks/error-boundaries

Disallow JSX inside a try block, where catch cannot see rendering errors

example.jsx1
function Dashboard({ data }) {
  try {
    return <Chart data={data} />;
  } catch {
    return <p>Could not draw the chart.</p>;
  }
}

react-hooks/exhaustive-depson by defaultfixable

Require the dependency array of a hook to list every value its callback reads

example.jsx1
function Profile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    loadUser(userId).then(setUser);
  }, []); // keeps showing the first user when userId changes

  return <h1>{user?.name}</h1>;
}

react-hooks/exhaustive-effect-dependencieshas suggestions

Require the dependency array of an effect to match what the effect reads

example.jsx1
import { useEffect } from "react";

function Room({ roomId }) {
  useEffect(() => {
    connect(roomId);
  }, []); // stays in the first room
}

react-hooks/fbt

Reserved for problems with fbt translations in the React Compiler: reports nothing

react/forbid-component-props

Disallow chosen props, by default className and style, on components

example.jsx2
<Button className="wide" style={{ color: "red" }}>
  Delete
</Button>;

react/forbid-dom-props

Disallow the props you choose on DOM elements such as div

example.jsx1
<div id="sidebar" />;

react/forbid-elements

Disallow the elements and components you choose

example.jsx1
<button onClick={save}>Save</button>;

react/forbid-foreign-prop-types

Disallow reading the propTypes of another component

example.jsx1
import Button from "./Button";

function IconButton(props) {
  return <Button {...props} />;
}
IconButton.propTypes = {
  ...Button.propTypes,
  icon: PropTypes.string,
};

react/forbid-prop-types

Disallow vague prop types such as any, array, and object

example.jsx1
import PropTypes from "prop-types";

function UserList({ users }) {
  return <ul>{users.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
}

UserList.propTypes = {
  users: PropTypes.array.isRequired,
};

react/forward-ref-uses-refhas suggestions

Require a component wrapped in forwardRef to take a ref parameter

example.jsx1
import { forwardRef } from "react";

const TextInput = forwardRef(props => {
  return <input type="text" value={props.value} />;
});

react/function-component-definitionfixable

Enforce one way to write function components: declaration, expression, or arrow function

example.jsx2
const Greeting = props => {
  return <h1>Hello, {props.name}</h1>;
};

const Footer = function (props) {
  return <footer>{props.text}</footer>;
};

react-hooks/gating

Validate the directive that puts a compiled component behind a feature flag

react-hooks/globals

Disallow reassigning variables declared outside a component or hook during render

example.jsx1
let renderCount = 0;

function Counter() {
  renderCount = renderCount + 1;
  return <div>Rendered {renderCount} times</div>;
}

react/hook-use-statehas suggestions

Require useState to be destructured into a [value, setValue] pair

example.jsx3
import React, { useState } from "react";

function Picker() {
  const color = useState("red");
  const [size, updateSize] = React.useState(1);
  const [open] = useState(false);
  return <div />;
}

react-hooks/hooks

Require hooks to be called at the top level of a component or hook, never conditionally

example.jsx1
import { useState } from "react";

function Profile({ user }) {
  if (!user) {
    return null;
  }
  const [name, setName] = useState(user.name);
  return <input value={name} onChange={event => setName(event.target.value)} />;
}

react/iframe-missing-sandbox

Require a valid sandbox attribute on iframe elements

example.jsx3
const map = <iframe src="https://maps.example.com" />;

const video = <iframe src="https://video.example.com" sandbox="allow-everything" />;

const widget = <iframe src="/widget" sandbox="allow-scripts allow-same-origin" />;

react-hooks/immutability

Disallow modifying props, state, and other values that React expects to stay unchanged

example.jsx1
function Price({ product }) {
  product.price = product.price / 2;
  return <span>{product.price}</span>;
}

react-hooks/incompatible-library

Report calls to library APIs that the React Compiler cannot memoize safely

example.jsx1
import { useForm } from "react-hook-form";

function Signup() {
  const { register, watch } = useForm();
  const email = watch("email");
  return <input {...register("email")} title={email} />;
}

react-hooks/invariant

Report code that makes the React Compiler fail one of its internal checks

example.jsx1
function Results(props) {
  let rows = [];
  let total = 0;
  ({ rows, total } = props.load());
  const logRows = () => console.log(rows);
  return <div onClick={logRows}>{total}</div>;
}

react/jsx-boolean-valuefixable

Enforce one way to write boolean attributes in JSX: disabled or disabled={true}

example.jsx1
const field = <input type="checkbox" checked={true} />;

react/jsx-child-element-spacing

Disallow a line break between text and an inline element in JSX, where a space may be lost

example.jsx1
const help = (
  <p>
    Read the
    <a href="/docs">docs</a>
  </p>
);

react/jsx-closing-bracket-locationfixable

Enforce where the closing bracket of a multiline JSX tag goes

example.jsx1
const button = (
  <Button
    label="Save"
    onClick={save} />
);

react/jsx-closing-tag-locationfixable

Enforce that the closing tag of a multiline JSX element lines up with its opening tag

example.jsx1
const card = (
  <Card>
    Welcome back</Card>
);

react/jsx-curly-brace-presencefixable

Disallow curly braces around plain strings in JSX, or require them

example.jsx4
const button = <Button label={"Save"} title={`Save`}>{"Save"}</Button>;

const panel = <Panel>{<Spinner />}</Panel>;

react/jsx-curly-newlinefixable

Enforce consistent line breaks inside the curly braces of JSX expressions

example.jsx1
const list = (
  <ul>
    {items.map(item => <li key={item.id}>{item.name}</li>)
    }
  </ul>
);

react/jsx-curly-spacingfixable

Enforce or disallow spaces inside the curly braces of JSX attributes and children

example.jsx4
const field = <Input value={ name } { ...rest } />;

react/jsx-equals-spacingfixable

Enforce or disallow spaces around the = of JSX attributes

example.jsx2
const field = <input name = "email" />;

react/jsx-filename-extension

Restrict the file extensions that may contain JSX

Button.js1
export function Button({ label }) {
  return <button>{label}</button>;
}

react/jsx-first-prop-new-linefixable

Enforce the position of the first prop of a JSX element

example.jsx1
const field = (
  <Input name="email"
    value={email}
  />
);

react/jsx-fragmentsfixable

Enforce <> or <React.Fragment> for React fragments

example.jsx2
import React, { Fragment } from "react";

const name = (
  <React.Fragment>
    <First />
    <Last />
  </React.Fragment>
);

const address = (
  <Fragment>
    <Street />
    <City />
  </Fragment>
);

react/jsx-handler-names

Enforce naming conventions for event handlers and the props they are passed to

example.jsx2
class Form extends React.Component {
  render() {
    return <Input onChange={this.updateName} blur={this.handleBlur} />;
  }
}

react/jsx-indentfixable

Enforce consistent indentation of JSX elements and their children

example.jsx2
const page = (
    <main>
      <Header />
            <p>Welcome</p>
    </main>
);

react/jsx-indent-propsfixable

Enforce consistent indentation of JSX props

example.jsx2
const field = (
    <Input
      name="email"
            value={email}
    />
);

react/jsx-keyon by default

Require a key prop on elements in arrays and iterators

example.jsx3
const list = (
  <ul>
    {users.map(user => <li>{user.name}</li>)}
  </ul>
);

const tabs = <nav>{[<Tab key="home" />, <Tab />]}</nav>;

const stars = Array.from({ length: 5 }, () => <Star />);

react/jsx-max-depth

Enforce a maximum depth of nested JSX elements

example.jsx1
const page = (
  <main>
    <section>
      <ul>
        <li>First</li>
      </ul>
    </section>
  </main>
);

react/jsx-max-props-per-linefixable

Enforce a maximum number of props on one line of a JSX tag

example.jsx1
const field = <Input name="email" value={email} />;

react/jsx-newlinefixable

Require or disallow an empty line between JSX elements and expressions

example.jsx2
const page = (
  <main>
    <Header />
    <Content />
    {footer}
  </main>
);

react/jsx-no-bind

Disallow .bind() and functions created during render as JSX props

example.jsx3
function Toolbar({ onSave, doc }) {
  const handleReset = () => doc.reset();

  return (
    <div>
      <Button onClick={() => onSave(doc)} />
      <Button onClick={doc.print.bind(doc)} />
      <Button onClick={handleReset} />
    </div>
  );
}

react/jsx-no-comment-textnodes

Disallow // and /* */ comments written as text between JSX tags

example.jsx2
const header = (
  <header>
    // TODO: add the logo
    <h1>Welcome</h1>
    /* the subtitle goes here */
  </header>
);

react/jsx-no-constructed-context-values

Disallow context provider values that are created anew on every render

example.jsx1
function App({ user, children }) {
  return (
    <UserContext.Provider value={{ user, isAdmin: user.role === "admin" }}>
      {children}
    </UserContext.Provider>
  );
}

react/jsx-no-duplicate-props

Disallow the same prop twice on one JSX element

example.jsx1
const field = <Input name="email" type="text" name="username" />;

react/jsx-no-leaked-renderfixable

Disallow && conditions in JSX that can render 0 or NaN

example.jsx2
function Cart({ items, note }) {
  return (
    <Panel footer={note && <Note text={note} />}>
      {items.length && <List items={items} />}
    </Panel>
  );
}

react/jsx-no-literals

Disallow text written directly in JSX

example.jsx1
function Greeting() {
  return <p>Welcome back</p>;
}

react/jsx-no-script-url

Disallow javascript: URLs in links

example.jsx1
const close = <a href="javascript:closeDialog()">Close</a>;

react/jsx-no-target-blankfixable

Require rel="noreferrer" on links that open in a new tab

example.jsx1
const docs = <a href="https://example.com/docs" target="_blank">Docs</a>;

react/jsx-no-undef

Disallow components in JSX that are not declared or imported

example.jsx1
export function Page() {
  return <Header title="Home" />;
}

react/jsx-no-useless-fragmentfixable

Disallow fragments that wrap a single child or sit directly in an HTML element

example.jsx1
function Avatar({ user }) {
  return (
    <>
      <img src={user.photo} alt={user.name} />
    </>
  );
}

react/jsx-pascal-case

Require component names in JSX to be in PascalCase

example.jsx2
const page = (
  <User_Card>
    <NAVBAR />
  </User_Card>
);

react/jsx-props-no-multi-spacesfixable

Disallow more than one space, or an empty line, between JSX props

example.jsx2
const button = <Button  type="submit"   disabled />;

react/jsx-props-no-spread-multifixable

Disallow spreading the same variable twice in one JSX element

example.jsx1
function SaveButton(props) {
  return <Button {...props} type="submit" {...props} />;
}

react/jsx-sort-default-propsdeprecated

Require the keys of defaultProps to be sorted alphabetically

example.jsx1
function Button({ label, size }) {
  return <button className={size}>{label}</button>;
}

Button.defaultProps = {
  size: "medium",
  label: "OK",
};

react/jsx-sort-propsfixable

Require JSX props to be sorted alphabetically

example.jsx1
const button = <Button type="submit" disabled />;

react/jsx-tag-spacingfixable

Enforce spacing around the <, / and > of JSX tags

example.jsx4
const small = <Avatar/>;
const medium = < Avatar />;
const large = <Avatar / >;
const label = <p>Profile< /p>;

react/jsx-wrap-multilinesfixable

Require parentheses around JSX that spans several lines

example.jsx1
function Card({ title }) {
  return <section>
    <h2>{title}</h2>
  </section>;
}

react-hooks/memo-dependencieshas suggestions

Require the dependency list of useMemo and useCallback to match what the callback reads

example.jsx1
import { useMemo } from "react";

function Results({ items, query }) {
  const visible = useMemo(() => items.filter(item => item.name.includes(query)), [items]);
  return <List items={visible} />;
}

react/no-access-state-in-setstate

Disallow reading this.state in the argument of this.setState()

example.jsx1
class Counter extends React.Component {
  increment() {
    this.setState({ count: this.state.count + 1 });
  }
}

react/no-adjacent-inline-elements

Disallow inline elements that touch each other without whitespace between them

example.jsx1
const nav = (
  <div>
    <a href="/login">Log in</a><a href="/signup">Sign up</a>
  </div>
);

react/no-array-index-key

Disallow the index of an array item as its key

example.jsx1
const list = todos.map((todo, index) => <Todo key={index} {...todo} />);

react/no-arrow-function-lifecyclefixable

Disallow arrow functions in class fields as React lifecycle methods

example.jsx2
class Clock extends React.Component {
  componentDidMount = () => {
    this.timer = setInterval(this.tick, 1000);
  };

  render = () => <time>{this.state.now}</time>;
}

react/no-children-prop

Disallow passing children as a prop

example.jsx1
const card = <Card children={<Avatar user={user} />} />;

react/no-clone-element

Disallow React.cloneElement

example.jsx2
import React, { Children, cloneElement } from "react";

function List({ children, selected }) {
  return Children.map(children, (child, i) => cloneElement(child, { isSelected: i === selected }));
}

const disabled = React.cloneElement(<Button />, { disabled: true });

react/no-danger

Disallow the dangerouslySetInnerHTML prop

example.jsx1
const comment = <div dangerouslySetInnerHTML={{ __html: post.body }} />;

react/no-danger-with-children

Disallow dangerouslySetInnerHTML on an element that also has children

example.jsx2
const article = <div dangerouslySetInnerHTML={{ __html: post.body }}>Loading</div>;

const summary = <div dangerouslySetInnerHTML={{ __html: post.summary }} children="Loading" />;

react/no-deprecated

Disallow React APIs that are deprecated

example.jsx1
import ReactDOM from "react-dom";

ReactDOM.render(<App />, document.getElementById("root"));

react-hooks/no-deriving-state-in-effects

Disallow effects that only compute state from props or other state

example.jsx1
import { useEffect, useState } from "react";

function Greeting({ firstName, lastName }) {
  const [fullName, setFullName] = useState("");

  useEffect(() => {
    setFullName(firstName + " " + lastName);
  }, [firstName, lastName]);

  return <h1>{fullName}</h1>;
}

react/no-did-mount-set-state

Disallow this.setState() in componentDidMount

example.jsx1
class Profile extends React.Component {
  componentDidMount() {
    this.setState({ name: this.props.name.toUpperCase() });
  }
}

react/no-did-update-set-state

Disallow this.setState() in componentDidUpdate

example.jsx1
class Cart extends React.Component {
  componentDidUpdate(previous) {
    if (previous.items !== this.props.items) {
      this.setState({ total: sum(this.props.items) });
    }
  }
}

react/no-direct-mutation-state

Disallow assigning to this.state outside of the constructor

example.jsx2
class Counter extends React.Component {
  increment() {
    this.state.count++;
  }

  rename(name) {
    this.state.user.name = name;
  }
}

react/no-find-dom-node

Disallow findDOMNode

example.jsx1
import { findDOMNode } from "react-dom";

class SearchBox extends React.Component {
  componentDidMount() {
    findDOMNode(this).focus();
  }

  render() {
    return <input type="search" />;
  }
}

react/no-invalid-html-attributehas suggestions

Disallow values of the rel attribute that HTML does not define

example.jsx3
const docs = <a href="https://example.com" rel="noopenner">Docs</a>;

const home = <a href="/" rel="stylesheet">Home</a>;

const logo = <img src="/logo.png" rel="preload" />;

react/no-is-mounted

Disallow this.isMounted()

example.jsx1
class Profile extends React.Component {
  componentDidMount() {
    loadUser(this.props.id).then(user => {
      if (this.isMounted()) {
        this.setState({ user });
      }
    });
  }
}

react/no-multi-comp

Disallow more than one React component in a file

example.jsx1
function Avatar({ user }) {
  return <img src={user.photo} alt={user.name} />;
}

function Profile({ user }) {
  return <Avatar user={user} />;
}

react/no-namespace

Disallow namespaced element names such as <svg:circle>

example.jsx2
const dot = <svg:circle r="4" />;

const line = React.createElement("svg:line");

react/no-object-type-as-default-prop

Disallow objects, arrays, and functions as default values of destructured props

example.jsx2
function UserList({ users = [], onSelect = () => {} }) {
  return <List items={users} onSelect={onSelect} />;
}

react/no-react-children

Disallow the React.Children methods

example.jsx1
import { Children } from "react";

function Menu({ children }) {
  return (
    <ul>
      {Children.map(children, child => (
        <li>{child}</li>
      ))}
    </ul>
  );
}

react/no-redundant-should-component-update

Disallow shouldComponentUpdate in a class that extends React.PureComponent

example.jsx1
class Price extends React.PureComponent {
  shouldComponentUpdate(nextProps) {
    return nextProps.amount !== this.props.amount;
  }

  render() {
    return <span>{this.props.amount}</span>;
  }
}

react/no-render-return-value

Disallow using the return value of ReactDOM.render

example.jsx1
const app = ReactDOM.render(<App />, document.body);

react/no-set-state

Disallow this.setState in class components

example.jsx1
class Counter extends React.Component {
  increment() {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return <button onClick={() => this.increment()}>{this.state.count}</button>;
  }
}

react/no-string-refs

Disallow strings as the value of the ref attribute

example.jsx1
class SearchBox extends React.Component {
  componentDidMount() {
    this.refs.input.focus();
  }

  render() {
    return <input ref="input" />;
  }
}

react/no-this-in-sfc

Disallow this in function components

example.jsx1
function Greeting(props) {
  return <h1>Hello, {this.props.name}</h1>;
}

react/no-typos

Disallow misspelled lifecycle methods, static properties, and prop types

example.jsx3
class Clock extends React.Component {
  static DefaultProps = { interval: 1000 };

  getDerivedStateFromProps(props) {
    return { interval: props.interval };
  }

  componentDidmount() {
    this.start();
  }
}

react/no-unescaped-entitieshas suggestions

Disallow " and ' as plain characters in JSX text

example.jsx1
const label = <label htmlFor="name">" Your name</label>;

react/no-unknown-propertyfixable

Disallow attributes on DOM elements that React does not know

example.jsx3
const field = (
  <label class="field" for="email">
    Email <input id="email" tabindex="1" />
  </label>
);

react/no-unsafe

Disallow the UNSAFE_ lifecycle methods of class components

example.jsx1
class Feed extends React.Component {
  UNSAFE_componentWillMount() {
    this.subscription = subscribe(this.props.channel);
  }
}

react/no-unstable-nested-components

Disallow defining a component inside another component

example.jsx1
function UserList({ users }) {
  function Row({ user }) {
    return <li>{user.name}</li>;
  }

  return (
    <ul>
      {users.map(user => (
        <Row key={user.id} user={user} />
      ))}
    </ul>
  );
}

react/no-unused-class-component-methods

Disallow methods and properties of a class component that the class never uses

example.jsx2
class SearchBox extends React.Component {
  timeout = 300;

  handleChange(event) {
    this.props.onSearch(event.target.value);
  }

  render() {
    return <input />;
  }
}

react/no-unused-prop-types

Disallow props that are declared but never used in the component

example.jsx1
function Avatar({ name }) {
  return <span>{name}</span>;
}

Avatar.propTypes = {
  name: PropTypes.string,
  size: PropTypes.number,
};

react/no-unused-state

Disallow fields of a class component's state that are never read

example.jsx1
class Counter extends React.Component {
  state = { count: 0, lastClick: null };

  render() {
    return <button>{this.state.count}</button>;
  }
}

react/no-will-update-set-state

Disallow this.setState in componentWillUpdate

example.jsx1
class Price extends React.Component {
  componentWillUpdate(nextProps) {
    this.setState({ total: nextProps.amount * nextProps.quantity });
  }
  render() {
    return <span>{this.state.total}</span>;
  }
}

react/only-export-components

Require files with components to export only components, so Fast Refresh works

example.jsx1
export function formatPrice(cents) {
  return `$${(cents / 100).toFixed(2)}`;
}

export function Price({ cents }) {
  return <span>{formatPrice(cents)}</span>;
}

react/prefer-es6-class

Enforce one way to write class components: class or createReactClass

example.jsx1
const Greeting = createReactClass({
  render() {
    return <h1>Hello {this.props.name}</h1>;
  },
});

react/prefer-exact-props

Require propTypes to be wrapped in a function that rejects unknown props

example.jsx1
function Button({ label }) {
  return <button>{label}</button>;
}

Button.propTypes = {
  label: PropTypes.string,
};

react/prefer-function-component

Require function components instead of class components

example.jsx1
class Counter extends React.Component {
  state = { count: 0 };
  render() {
    return <button onClick={() => this.setState({ count: this.state.count + 1 })}>{this.state.count}</button>;
  }
}

react/prefer-read-only-propsfixable

Require the props in a component's type to be readonly

example.tsx2
type Props = {
  name: string;
  age?: number;
};

function Profile(props: Props) {
  return <p>{props.name} ({props.age})</p>;
}

react/prefer-stateless-function

Require class components that only render from props to be functions

example.jsx1
class Greeting extends React.Component {
  render() {
    return <h1>Hello {this.props.name}</h1>;
  }
}

react-hooks/preserve-manual-memoization

Disallow useMemo and useCallback calls that React Compiler cannot keep memoized

example.jsx1
import { useMemo } from "react";

function Table({ rows }) {
  const columns = ["name"];
  const isWide = useIsWide();
  if (isWide) {
    columns.push("email");
  }
  const header = useMemo(() => columns.map(name => name.toUpperCase()), [columns]);
  return <Grid header={header} rows={rows} />;
}

react/prop-types

Require every prop that a component uses to be declared

example.jsx1
function Greeting({ name }) {
  return <h1>Hello {name}</h1>;
}

react-hooks/purity

Disallow calls such as Math.random() and Date.now() while a component renders

example.jsx1
function Clock() {
  const now = Date.now();
  return <time>{now}</time>;
}

react/react-in-jsx-scope

Require React to be in scope in files that use JSX

example.jsx1
export function App() {
  return <h1>Hello</h1>;
}

react-hooks/refs

Disallow reading or writing ref.current while a component renders

example.jsx3
import { useRef } from "react";

function Counter() {
  const renders = useRef(0);
  renders.current = renders.current + 1;
  return <p>Rendered {renders.current} times</p>;
}

react/require-default-props

Require a default value for every prop that is not required

example.jsx1
function Greeting({ name, punctuation }) {
  return <h1>Hello {name}{punctuation}</h1>;
}

Greeting.propTypes = {
  name: PropTypes.string.isRequired,
  punctuation: PropTypes.string,
};

react/require-optimization

Require class components to define shouldComponentUpdate or extend PureComponent

example.jsx1
class Row extends React.Component {
  render() {
    return <li>{this.props.label}</li>;
  }
}

react/require-render-return

Require the render method of a class component to return something

example.jsx1
class Greeting extends React.Component {
  render() {
    <h1>Hello {this.props.name}</h1>;
  }
}

react-hooks/rule-suppression

Disallow comments that turn off rules-of-hooks or exhaustive-deps

example.jsx1
import { useEffect } from "react";

function Title({ text }) {
  useEffect(() => {
    document.title = text;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
  return <h1>{text}</h1>;
}

react-hooks/rules-of-hookson by default

Require hooks to be called at the top level of a component or a custom hook

example.jsx1
function Profile({ user }) {
  if (user) {
    const [editing, setEditing] = useState(false);
  }
  return <p>{user?.name}</p>;
}

react/self-closing-compfixable

Require elements without children to be self-closing

example.jsx3
const avatar = <Avatar user={user}></Avatar>;

const spacer = <div className="spacer"></div>;

const list = (
  <UserList users={users}>
  </UserList>
);

react-hooks/set-state-in-effect

Disallow calling a state setter directly in the body of an effect

example.jsx1
import { useEffect, useState } from "react";

function FullName({ first, last }) {
  const [fullName, setFullName] = useState("");

  useEffect(() => {
    setFullName(first + " " + last);
  }, [first, last]);

  return <span>{fullName}</span>;
}

react-hooks/set-state-in-render

Disallow calling a state setter unconditionally while a component renders

example.jsx1
import { useState } from "react";

function Cart({ items }) {
  const [count, setCount] = useState(0);
  setCount(items.length);
  return <span>{count}</span>;
}

react/sort-comp

Enforce the order of methods and properties in a class component

example.jsx1
class Profile extends React.Component {
  render() {
    return <button onClick={this.handleClick}>{this.props.name}</button>;
  }

  handleClick() {
    this.props.onSelect();
  }

  componentDidMount() {
    this.props.load();
  }
}

react/sort-default-props

Require the keys of defaultProps to be sorted alphabetically

example.jsx1
function Button({ label, size }) {
  return <button className={size}>{label}</button>;
}

Button.defaultProps = {
  size: "medium",
  label: "OK",
};

react/sort-prop-typesfixable

Require the keys of propTypes to be sorted alphabetically

example.jsx1
function Button({ label, size }) {
  return <button className={size}>{label}</button>;
}

Button.propTypes = {
  size: PropTypes.string,
  label: PropTypes.string,
};

react/state-in-constructor

Enforce where a class component sets its initial state: in the constructor or in a class field

example.jsx1
class Counter extends React.Component {
  state = { count: 0 };

  render() {
    return <span>{this.state.count}</span>;
  }
}

react-hooks/static-components

Disallow components that are created while another component renders

example.jsx1
function Form({ label }) {
  const Field = () => <input placeholder={label} />;
  return <Field />;
}

react/static-property-placement

Enforce where a class component declares propTypes, defaultProps and other static properties

example.jsx2
class Button extends React.Component {
  static get defaultProps() {
    return { label: "OK" };
  }

  render() {
    return <button>{this.props.label}</button>;
  }
}

Button.propTypes = {
  label: PropTypes.string,
};

react/style-prop-object

Require the value of the style prop to be an object

example.jsx3
const title = <h1 style="color: red">Settings</h1>;

const css = "margin-top: 8px";
const body = <p style={css}>Saved</p>;

const footer = React.createElement("footer", { style: "padding: 4px" });

react-hooks/syntaxhas suggestions

Disallow invalid JavaScript in components and hooks, such as assigning to a const

example.jsx1
function Total({ items }) {
  const total = 0;
  total = items.length;
  return <span>{total}</span>;
}

react-hooks/todo

Report valid syntax that the React Compiler does not support yet

example.jsx1
function Report({ load }) {
  let rows = [];
  try {
    rows = load();
  } finally {
    log("loaded");
  }
  return <Table rows={rows} />;
}

react-hooks/unsupported-syntax

Disallow syntax that the React Compiler cannot compile, such as eval()

example.jsx1
function Calculator({ formula }) {
  const result = eval(formula);
  return <output>{result}</output>;
}

react-hooks/use-memo

Require useMemo() callbacks to take no parameters, be synchronous, and not reassign outer variables

example.jsx1
import { useMemo } from "react";

function Report({ id }) {
  const rows = useMemo(async () => {
    return await loadRows(id);
  }, [id]);
  return <Table rows={rows} />;
}

react/void-dom-elements-no-children

Disallow children on void DOM elements such as <img /> and <br />

example.jsx4
const field = <input name="email">Email</input>;

const photo = <img src={url} children="A photo" />;

const rule = <hr dangerouslySetInnerHTML={{ __html: "Section" }} />;

const lineBreak = React.createElement("br", {}, "Next line");

react-hooks/void-use-memo

Require useMemo() callbacks to return a value, and the value to be used

example.jsx1
import { useMemo } from "react";

function Report({ rows }) {
  const visible = useMemo(() => {
    rows.filter(row => !row.hidden);
  }, [rows]);
  return <Table rows={visible} />;
}

React performance 4 rules

react-perf/jsx-no-jsx-as-prop

Disallow JSX elements created during render as the value of a JSX prop

example.jsx1
function Toolbar() {
  return <Button icon={<SaveIcon />} />;
}

react-perf/jsx-no-new-array-as-prop

Disallow arrays created during render as the value of a JSX prop

example.jsx1
function Settings() {
  return <Select options={["light", "dark"]} />;
}

react-perf/jsx-no-new-function-as-prop

Disallow functions created during render as the value of a JSX prop

example.jsx1
function Editor({ save }) {
  return <Button onClick={() => save("draft")} />;
}

react-perf/jsx-no-new-object-as-prop

Disallow objects created during render as the value of a JSX prop

example.jsx1
function Profile({ user }) {
  return <Avatar user={user} style={{ width: 32 }} />;
}

Accessibility (jsx-a11y) 36 rules

jsx-a11y/alt-text

Require alternative text on img, area, object and input type="image"

example.jsx4
const avatar = <img src={user.photo} />;
const divider = <img src="/line.png" role="presentation" />;
const search = <input type="image" src="/search.png" />;
const report = <object data="/report.pdf" alt="Annual report" />;

jsx-a11y/anchor-ambiguous-text

Disallow link text such as "click here" that says nothing about the target

example.jsx3
const pricing = <p>To see our prices, <a href="/pricing">click here</a>.</p>;
const docs = <a href="/docs">Learn more!</a>;
const home = <a href="/"><img src="/home.png" alt="link" /></a>;

jsx-a11y/anchor-has-contentfixable

Require links to have content that a screen reader can read

example.jsx2
const profile = <a href="/profile" />;
const settings = <a href="/settings"><span aria-hidden>Settings</span></a>;

jsx-a11y/anchor-is-valid

Require links to have a real href, and buttons to be button elements

example.jsx3
const home = <a>Home</a>;
const top = <a href="#">Back to top</a>;
const save = <a href="#" onClick={saveDraft}>Save</a>;

jsx-a11y/aria-propsfixable

Disallow aria-* attributes that do not exist

example.jsx2
const name = <input aria-labeledby="name-label" />;
const menu = <button aria-open={isOpen}>Menu</button>;

jsx-a11y/aria-proptypes

Require aria-* attributes to have a value of the right kind

example.jsx4
const toggle = <button aria-expanded="yes">Menu</button>;
const title = <div role="heading" aria-level="two">Billing</div>;
const column = <th aria-sort="up">Name</th>;
const status = <div aria-live="loud">{message}</div>;

jsx-a11y/aria-role

Require role to be a real ARIA role that is not abstract

example.jsx4
const picker = <div role="datepicker">{days}</div>;
const slider = <div role="range" />;
const panel = <div role="">{children}</div>;
const toggle = <div role="switch toggle" aria-checked={isOn} tabIndex={0} />;

jsx-a11y/aria-unsupported-elementsfixable

Disallow role and aria-* attributes on elements that do not support them

example.jsx2
const head = (
  <head>
    <meta charSet="utf-8" aria-hidden="true" />
    <title role="heading">Dashboard</title>
  </head>
);

jsx-a11y/autocomplete-valid

Require autoComplete on an input to be a value that browsers know

example.jsx2
const email = <input type="email" autoComplete="mail" />;
const password = <input type="password" autoComplete="password" />;

jsx-a11y/click-events-have-key-events

Require a keyboard handler next to onClick on elements that are not controls

example.jsx2
const row = <div onClick={openDetails}>{item.name}</div>;
const more = <a onClick={loadMore}>Load more</a>;

jsx-a11y/control-has-associated-label

Require buttons, links and other controls to have a text label

example.jsx2
const close = <button onClick={closeDialog}><img src="/x.svg" /></button>;
const next = <div role="button" tabIndex={0} onClick={goNext} />;

jsx-a11y/heading-has-content

Require headings to have content that a screen reader can read

example.jsx2
const title = <h1 className="hero" />;
const section = <h2><span aria-hidden>Pricing</span></h2>;

jsx-a11y/html-has-lang

Require a lang attribute on the html element

example.jsx2
function Layout({ children }) {
  return <html><body>{children}</body></html>;
}

function EmptyLang({ children }) {
  return <html lang=""><body>{children}</body></html>;
}

jsx-a11y/iframe-has-title

Require a title on iframe elements

example.jsx3
const map = <iframe src={mapUrl} />;
const video = <iframe src={videoUrl} title="" />;
const widget = <iframe {...frameProps} />;

jsx-a11y/img-redundant-alt

Disallow the words "image", "photo" and "picture" in alt text

example.jsx2
const cover = <img src="/lighthouse.jpg" alt="Photo of a lighthouse" />;
const avatar = <img src={user.photo} alt={`Picture of ${user.name}`} />;

jsx-a11y/interactive-supports-focushas suggestions

Require elements with an interactive role and an event handler to be focusable

example.jsx2
const save = <div role="button" onClick={saveDraft}>Save</div>;
const tab = <span role="tab" onClick={showBilling}>Billing</span>;

jsx-a11y/label-has-associated-control

Require every <label> to have text and a form control it belongs to

example.jsx2
const form = (
  <form>
    <label>Email</label>
    <input type="email" id="email" />

    <label htmlFor="password" />
    <input type="password" id="password" />
  </form>
);

jsx-a11y/lang

Require the lang attribute of <html> to be a valid language tag

example.jsx3
const wrong = <html lang="english" />;
const empty = <html lang="" />;
const missing = <html />;

jsx-a11y/media-has-caption

Require <audio> and <video> to have a <track> with captions

example.jsx1
const player = <video src="/intro.mp4" controls />;

jsx-a11y/mouse-events-have-key-events

Require onFocus with onMouseOver and onBlur with onMouseOut

example.jsx2
const help = (
  <button onMouseOver={showTooltip} onMouseOut={hideTooltip}>
    Help
  </button>
);

jsx-a11y/no-access-keyhas suggestions

Disallow the accessKey attribute

example.jsx2
const save = <button accessKey="s">Save</button>;
const open = <MenuItem accesskey="o" />;

jsx-a11y/no-aria-hidden-on-focusablefixable

Disallow aria-hidden="true" on elements that can receive focus

example.jsx2
const close = <button aria-hidden="true" onClick={closeDialog} />;
const panel = <div aria-hidden="true" tabIndex={0} />;

jsx-a11y/no-autofocushas suggestions

Disallow the autoFocus attribute

example.jsx1
const search = <input type="search" autoFocus />;

jsx-a11y/no-distracting-elements

Disallow <marquee> and <blink>

example.jsx2
const banner = <marquee>Free shipping this week</marquee>;
const warning = <blink>Sale ends today</blink>;

jsx-a11y/no-noninteractive-element-interactions

Disallow mouse and keyboard handlers on non-interactive elements

example.jsx2
const item = <li onClick={() => select(user)}>{user.name}</li>;
const card = <div role="article" onKeyDown={openCard} />;

jsx-a11y/no-noninteractive-tabindex

Disallow tabIndex of 0 or more on non-interactive elements

example.jsx2
const summary = <article tabIndex={0}>{post.summary}</article>;
const box = <div tabIndex="0" />;

jsx-a11y/no-redundant-rolesfixable

Disallow a role that the element already has

example.jsx3
const save = <button role="button">Save</button>;
const items = <ul role="list" />;
const volume = <input type="range" role="slider" />;

jsx-a11y/no-static-element-interactions

Require a role on <div>, <span> and similar elements that have event handlers

example.jsx2
const close = <div className="close" onClick={closeDialog}>Close</div>;
const more = <a onClick={loadMore}>Show more</a>;

jsx-a11y/prefer-tag-over-role

Require the HTML element instead of a role that imitates it

example.jsx4
const save = <div role="button" onClick={saveDraft}>Save</div>;
const menu = <div role="navigation" />;
const title = <span role="heading" aria-level={2}>Settings</span>;
const post = <Card role="article" />;

jsx-a11y/role-has-required-aria-props

Require the aria-* attributes that an element's role needs

example.jsx2
const dark = <span role="switch" tabIndex={0} onClick={toggleTheme} />;
const title = <div role="heading">Settings</div>;

jsx-a11y/role-supports-aria-props

Disallow aria-* attributes that the element's role does not support

example.jsx3
const filter = <a href="/sale" aria-checked={onSale}>On sale</a>;
const items = <ul aria-expanded={isOpen} />;
const save = <div role="button" aria-required="true" />;

jsx-a11y/scopefixable

Disallow the scope attribute on elements other than <th>

example.jsx1
const row = (
  <tr>
    <td scope="row">Total</td>
    <td>{total}</td>
  </tr>
);

jsx-a11y/tabindex-no-positivehas suggestions

Disallow tabIndex values greater than 0

example.jsx2
const form = (
  <form>
    <input name="email" tabIndex={2} />
    <input name="name" tabIndex="1" />
  </form>
);

Next.js 21 rules

@next/next/google-font-display

Require a display parameter other than auto, block or fallback on Google Fonts URLs

example.jsx1
const font = <link href="https://fonts.googleapis.com/css2?family=Inter" rel="stylesheet" />;

@next/next/google-font-preconnect

Require rel="preconnect" on <link> elements that point to fonts.gstatic.com

example.jsx1
const hint = <link href="https://fonts.gstatic.com" />;

@next/next/inline-script-id

Require an id on next/script components that have inline content

example.jsx1
import Script from "next/script";

export default function Page() {
  return <Script>{`window.startedAt = Date.now();`}</Script>;
}

@next/next/next-script-for-ga

Require next/script instead of <script> to load Google Analytics

example.jsx1
export default function Analytics() {
  return <script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX" />;
}

@next/next/no-async-client-component

Disallow async components in files that start with "use client"

example.jsx1
"use client";

export default async function Profile({ id }) {
  const user = await fetchUser(id);
  return <h1>{user.name}</h1>;
}

@next/next/no-before-interactive-script-outside-document

Disallow the beforeInteractive strategy of next/script outside pages/_document.js

pages/index.jsx1
import Script from "next/script";

export default function Home() {
  return <Script src="https://example.com/consent.js" strategy="beforeInteractive" />;
}

@next/next/no-css-tags

Disallow <link rel="stylesheet"> for your own CSS files

example.jsx1
export default function Layout({ children }) {
  return (
    <div>
      <link rel="stylesheet" href="/styles/site.css" />
      {children}
    </div>
  );
}

@next/next/no-document-import-in-page

Disallow importing next/document outside pages/_document.js

pages/index.jsx1
import { Head } from "next/document";

export default function Home() {
  return (
    <Head>
      <meta name="theme-color" content="#111111" />
    </Head>
  );
}

@next/next/no-duplicate-head

Disallow more than one <Head> element in a file

example.jsx1
import { Head, Html, Main, NextScript } from "next/document";

export default function Document() {
  return (
    <Html>
      <Head>
        <meta name="theme-color" content="#111111" />
      </Head>
      <Head>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

@next/next/no-head-element

Disallow the <head> element in favor of Head from next/head

pages/about.jsx1
export default function About() {
  return (
    <div>
      <head>
        <title>About us</title>
      </head>
      <h1>About us</h1>
    </div>
  );
}

@next/next/no-head-import-in-document

Disallow importing next/head in pages/_document.js

pages/_document.jsx1
import { Html, Main, NextScript } from "next/document";
import Head from "next/head";

export default function Document() {
  return (
    <Html>
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

@next/next/no-img-element

Disallow the <img> element in favor of Image from next/image

example.jsx1
export default function Avatar({ user }) {
  return <img src="/avatars/default.png" alt={user.name} />;
}

@next/next/no-page-custom-font

Disallow Google Fonts <link> elements outside pages/_document.js

pages/index.jsx1
import Head from "next/head";

export default function Home() {
  return (
    <Head>
      <link href="https://fonts.googleapis.com/css2?family=Inter&display=optional" rel="stylesheet" />
    </Head>
  );
}

@next/next/no-script-component-in-head

Disallow <Script> as a child of Head from next/head

example.jsx1
import Head from "next/head";
import Script from "next/script";

export default function Home() {
  return (
    <Head>
      <title>Home</title>
      <Script src="https://example.com/widget.js" />
    </Head>
  );
}

@next/next/no-styled-jsx-in-document

Disallow <style jsx> in pages/_document.js

pages/_document.jsx1
import { Head, Html, Main, NextScript } from "next/document";

export default function Document() {
  return (
    <Html>
      <Head />
      <body>
        <Main />
        <NextScript />
        <style jsx>{`
          body {
            margin: 0;
          }
        `}</style>
      </body>
    </Html>
  );
}

@next/next/no-sync-scripts

Disallow <script src> without async or defer

example.jsx1
export default function Checkout() {
  return <script src="https://example.com/payments.js" />;
}

@next/next/no-title-in-document-head

Disallow <title> in the Head of next/document

pages/_document.jsx1
import { Head, Html, Main, NextScript } from "next/document";

export default function Document() {
  return (
    <Html>
      <Head>
        <title>My shop</title>
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

@next/next/no-typoshas suggestions

Disallow misspellings of getStaticProps, getStaticPaths and getServerSideProps

pages/posts.jsx1
export async function getStaticProp() {
  return { props: { posts: await loadPosts() } };
}

export default function Posts({ posts }) {
  return <PostList posts={posts} />;
}

@next/next/no-unwanted-polyfillio

Disallow scripts from polyfill.io, and polyfills that Next.js already ships

example.jsx1
export default function Polyfills() {
  return <script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver" />;
}

import 46 rules

import/default

Require a default import to match a default export in the imported file

example.js1
import save from "./storage.js";

save({ id: "draft" });

import/dynamic-import-chunknamehas suggestions

Require a webpackChunkName comment in every dynamic import()

example.js1
const settings = await import("./pages/settings.js");

import/enforce-node-protocol-usagefixable

Enforce either node:fs or fs when importing Node.js built-in modules

example.js4
import fs from "fs";
export { join } from "path";

const os = require("os");
const crypto = await import("crypto");

import/export

Disallow exporting the same name twice, and export * from a file with no named exports

example.js2
export * from "./dates.js";
export * from "./numbers.js";

import/exports-last

Require all exports to come after every other statement in the file

example.js1
export const retries = 3;

function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

export { wait };

import/extensions

Enforce that import paths always include the file extension, or never do

example.js1
import { createUser } from "./user";

import/firstfixable

Require all imports to come before every other statement in the file

example.js1
import { loadConfig } from "./config.js";

loadConfig(); // runs after "./server.js" has loaded, not before

import { startServer } from "./server.js";

startServer();

import/group-exports

Require all named exports to be in a single export declaration

example.js2
export const retries = 3;

export function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

import/imports-firstfixabledeprecated

Require all imports to come before every other statement (old name of import/first)

example.js1
import { loadConfig } from "./config.js";

loadConfig(); // runs after "./server.js" has loaded, not before

import { startServer } from "./server.js";

startServer();

import/max-dependencies

Enforce a maximum number of modules that a file can import

example.js1
import { readFile } from "node:fs/promises";
export { parse } from "./parser.js";

const { render } = await import("./render.js");

import/named

Require each named import to match a named export in the imported file

example.js2
import { fromatDate } from "./dates.js";
export { parseDate } from "./dates.js";

console.log(fromatDate(new Date()));

import/namespace

Require each property read from an import * as namespace to be exported by that file

example.js4
import * as api from "./api.js";

api.remove("draft");
const { update } = api;
api.save = () => {};

const action = "load";
api[action]("draft");

import/newline-after-importfixable

Require an empty line between the last import and the code that follows

example.js1
import { readFile } from "node:fs/promises";
import { join } from "node:path";
const root = join(import.meta.dirname, "..");

import/no-absolute-pathfixable

Disallow importing a module by an absolute file system path

example.js2
import config from "/home/ana/app/src/config.js";

const logger = require("/home/ana/app/src/logger.js");

import/no-amd

Disallow AMD define() and require() calls

example.js1
define(["./logger"], function (logger) {
  logger.info("ready");
});

import/no-commonjs

Disallow require(), module.exports and exports.* in favor of import and export

example.js2
const { parse } = require("./parse");

module.exports = { run: text => parse(text) };

import/no-cycle

Disallow importing a module that imports this file back, directly or through other modules

user.js1
import { currentUser } from "./session";

export const findUser = id => ({ id });
export const isCurrent = user => user.id === currentUser.id;

import/no-default-export

Disallow default exports

example.js1
export default function formatPrice(cents) {
  return `$${(cents / 100).toFixed(2)}`;
}

import/no-deprecated

Disallow importing and using names that are marked @deprecated

example.js2
import { fetchUser } from "./api";

fetchUser(1);

import/no-duplicatesfixable

Disallow importing the same module more than once in a file

example.js2
import { readFile } from "node:fs/promises";
import { join } from "node:path";
import { writeFile } from "node:fs/promises";

import/no-dynamic-require

Disallow require() with an argument that is not a string literal

example.js2
const plugin = require(name);
const locale = require(`./locales/${language}.json`);

import/no-import-module-exportsfixable

Disallow import declarations in a file that exports with module.exports or exports.*

example.js1
import { parse } from "./parse";

module.exports = { run: text => parse(text) };

import/no-internal-modules

Disallow importing a file from inside another directory or package

example.js1
import { listUsers } from "./api/users";

import/no-mutable-exports

Disallow exporting a variable declared with let or var

example.js1
export let count = 0;

export function increment() {
  count += 1;
}

import/no-named-as-default

Disallow naming a default import after a named export of the same module

example.js1
import Button from "./button";

import/no-named-as-default-member

Disallow reading a property of a default import that is also a named export of the module

example.js2
import config from "./config";

config.load("./app.json");
const { load } = config;

import/no-named-default

Disallow import { default as name } in favor of import name

example.js1
import { default as React, useState } from "react";

import/no-named-export

Disallow named exports, so that a module only has a default export

example.js2
export const taxRate = 0.2;

export function formatPrice(cents) {
  return `$${(cents / 100).toFixed(2)}`;
}

import/no-namespacefixable

Disallow import * as name in favor of named imports

example.js1
import * as utils from "./utils";

const width = utils.clamp(utils.sum(sizes), 0, 100);

import/no-nodejs-modules

Disallow importing Node.js builtin modules such as fs and path

example.js3
import fs from "fs";
import { join } from "node:path";

const os = require("os");

import/no-relative-packagesfixable

Disallow relative imports that reach into another package

packages/app/index.js1
import { Button } from "../ui/button.js";

import/no-restricted-paths

Disallow files in one folder from importing files in another

src/client/app.js1
import { query } from "../server/db.js";

import/no-self-import

Disallow a module from importing itself

cart.js1
import * as cart from "./cart.js";

export function total(items) {
  return items.length;
}

export function print(items) {
  console.log(cart.total(items));
}

import/no-unassigned-import

Disallow imports that bind no name

example.js3
import "./register-globals.js";
import {} from "lodash";
require("./patch-console.js");

import/no-unresolved

Require every import to point to a file or package that exists

example.js2
import { formatDate } from "./utils/fromat.js";
import leftPad from "a-package-that-is-not-installed";

import/no-unused-modules

Disallow exports that no other file imports, and files that export nothing

shop/format.js1
export function formatDate(date) {}
export function formatTime(date) {}

import/no-useless-path-segmentsfixable

Disallow path segments in imports that can be removed

src/pages/home.js2
import { formatDate } from "./../format.js";
import { Header } from "../pages/header.js";

import/no-webpack-loader-syntax

Disallow webpack loader syntax such as style-loader!./app.css in imports

example.js2
import styles from "style-loader!css-loader!./app.css";
const notes = require("raw-loader!./notes.txt");

import/orderfixable

Enforce a consistent order of imports, by group and optionally by name

example.js2
import { formatDate } from "./format.js";
import express from "express";
import fs from "node:fs";

import/prefer-default-export

Require a default export in a file that exports a single name

example.js1
export function formatDate(date) {
  return date.toISOString();
}

import/unambiguous

Require a module to have an import or export so it cannot be read as a script

example.js1
function greet(name) {
  console.log(`Hello, ${name}`);
}

greet("Ada");

unicorn 138 rules

unicorn/catch-error-namefixable

Enforce one name for the error in catch clauses and promise handlers

example.js3
try {
  await save(user);
} catch (e) {
  console.log(e.message);
}

save(user).catch(err => console.log(err.message));

save(user).then(done, ex => console.log(ex.message));

unicorn/consistent-assertfixable

Require assert.ok(value) instead of assert(value) with node:assert

example.js1
import assert from "node:assert";

assert(user.isActive);
assert.equal(user.name, "Ada");

unicorn/consistent-date-clonefixable

Require new Date(date) instead of new Date(date.getTime()) to copy a date

example.js1
const deadline = new Date(start.getTime());
deadline.setDate(deadline.getDate() + 7);

unicorn/consistent-empty-array-spreadhas suggestions

Require both branches of a spread ternary in an array to have the same type

example.js1
const args = ["build", ...(watch ? ["--watch", "--hot"] : "")];

unicorn/consistent-existence-index-checkfixable

Require === -1 and !== -1 to test the result of indexOf() and findIndex()

example.js2
const index = users.findIndex(user => user.id === id);

if (index < 0) {
  throw new Error("No such user");
}

if (index >= 0) {
  users.splice(index, 1);
}

unicorn/consistent-function-scoping

Require functions that use nothing from the enclosing function to be moved out of it

example.js1
export function totalPrice(items) {
  function withTax(price) {
    return price * 1.2;
  }

  return items.reduce((sum, item) => sum + withTax(item.price), 0);
}

unicorn/consistent-template-literal-escapefixable

Enforce one way to escape ${ in template literals

example.js2
const usage = `Write $\{name} to insert the name`;
const help = `Write \$\{name} to insert the name`;

unicorn/empty-brace-spacesfixable

Disallow spaces and line breaks inside empty braces

example.js3
const cache = { };

function noop() {
}

class Store { }

unicorn/error-message

Require a message when creating a built-in error

example.js3
function parsePort(value) {
  if (value === undefined) throw new Error();
  if (value === "") throw new TypeError("");
  if (value > 65_535) throw new RangeError({ value });
  return Number(value);
}

unicorn/escape-casefixable

Require uppercase hexadecimal digits in escape sequences

example.js4
const copyright = "\xa9 2026";
const price = `${amount}\u00a0EUR`;
const smile = "\u{1f600}";
const escape = /\x1b\[\d+m/;

unicorn/explicit-length-checkfixable

Require length and size to be compared with a number

example.js4
if (items.length) {
  render(items);
}

if (!selected.size) {
  disable(button);
}

const hasErrors = errors.length >= 1;
const first = items.length && items[0];

unicorn/explicit-timer-delayfixable

Require or disallow an explicit delay for setTimeout() and setInterval()

example.js2
setTimeout(() => input.focus());

window.setInterval(poll);

unicorn/filename-case

Enforce one case style for file names

src/userProfile.js1
export function userProfile() {}

unicorn/import-style

Enforce how each module is imported: named, default, namespace, or for side effects

example.js2
import { join } from "node:path";
import util from "node:util";

unicorn/max-nested-calls

Enforce a maximum depth of calls nested in the arguments of other calls

example.js1
save(normalize(parse(read(file))));

unicorn/new-for-builtins

Require new for built-in classes and disallow it for String, Number, Boolean, Symbol, BigInt

example.js4
const slots = Array(3);
const seen = Map();
const failure = Error("Not found");
const now = Date();

unicorn/no-abusive-eslint-disable

Require eslint-disable comments to name the rules they turn off

example.js3
console.log(user.name); // eslint-disable-line

// oxlint-disable-next-line
console.log(user.email);

/* eslint-disable */
console.log(user.id);
/* eslint-enable */

unicorn/no-accessor-recursion

Disallow a getter or setter that uses its own property on this

example.js2
class User {
  get name() {
    return this.name;
  }

  set name(value) {
    this.name = value.trim();
  }
}

unicorn/no-anonymous-default-export

Require a name for a function or class that is the default export

example.js1
export default function (user) {
  return `${user.first} ${user.last}`;
}

unicorn/no-array-callback-reference

Disallow passing a function by name to array methods such as map and filter

example.js1
const sizes = ["10", "10", "10"].map(parseInt); // [10, NaN, 2]

unicorn/no-array-for-each

Require a for...of loop instead of .forEach()

example.js1
users.forEach((user) => {
  if (!user.active) return;
  notify(user);
});

unicorn/no-array-method-this-argument

Disallow the thisArg argument of array methods such as map and filter

example.js1
const mine = items.filter(function (item) {
  return item.owner === this.id;
}, user);

unicorn/no-array-reduce

Disallow reduce() and reduceRight() in favor of a loop

example.js1
const byId = users.reduce((result, user) => {
  result[user.id] = user;
  return result;
}, {});

unicorn/no-array-reversehas suggestions

Require toReversed() instead of reverse(), which changes the array

example.js1
const newestFirst = posts.reverse();

unicorn/no-array-sorthas suggestions

Require toSorted() instead of sort(), which changes the array

example.js1
const ranked = players.sort((a, b) => b.score - a.score);

unicorn/no-await-expression-memberfixable

Disallow reading a property directly from an await expression

example.js2
async function show(id) {
  const name = (await getUser(id)).name;
  const first = (await getPosts(id))[0];
  render(name, first);
}

unicorn/no-await-in-promise-methodshas suggestions

Disallow await on the elements of the array passed to Promise.all() and similar methods

example.js2
async function loadPage(id) {
  const [user, posts] = await Promise.all([await loadUser(id), await loadPosts(id)]);
  render(user, posts);
}

unicorn/no-console-spacesfixable

Disallow spaces at the edges of strings where console.log() already adds one

example.js3
console.log("Total: ", total);
console.error(path, " was not found");
console.info(`Saved ${count} files `, "in", elapsed);

unicorn/no-hex-escapefixable

Require Unicode escapes such as \u001B instead of hexadecimal escapes such as \x1B

example.js4
const notice = "\xA9 2026 Example Inc.";
const reset = `\x1B[0m`;
const controlCharacters = /[\x00-\x1F]/;

unicorn/no-immediate-mutation

Disallow changing an array, object, Set, or Map in the statement right after creating it

example.js2
const fruits = ["apple", "pear"];
fruits.push("plum");

const user = { name: "Ada" };
user.role = "admin";

unicorn/no-instanceof-arrayfixable

Require Array.isArray() instead of instanceof Array

example.js1
function toList(value) {
  return value instanceof Array ? value : [value];
}

unicorn/no-instanceof-builtinshas suggestions

Disallow instanceof with built-in constructors such as Array, String, and Function

example.js2
function describe(value) {
  if (value instanceof String) return "text";
  if (value instanceof Function) return "callback";
}

unicorn/no-invalid-fetch-options

Disallow a body in fetch() and new Request() when the method is GET or HEAD

example.js1
function createUser(user) {
  return fetch("/api/users", {
    body: JSON.stringify(user),
  });
}

unicorn/no-invalid-remove-event-listener

Disallow passing a new function to removeEventListener()

example.js2
button.addEventListener("click", () => save());
button.removeEventListener("click", () => save());

window.addEventListener("resize", this.layout.bind(this));
window.removeEventListener("resize", this.layout.bind(this));

unicorn/no-lonely-if

Disallow an if as the only statement in another if when neither has an else

example.js1
if (user) {
  if (user.isAdmin) {
    showSettings();
  }
}

unicorn/no-negated-conditionfixable

Disallow a negated condition in an if with an else and in a ternary

example.js2
if (!user) {
  showLogin();
} else {
  showProfile(user);
}

const label = count !== 1 ? "items" : "item";

unicorn/no-nested-ternaryfixable

Disallow deeply nested ternaries, and require parentheses around a ternary in a ternary

example.js1
const size = width > 1200 ? "large" : width > 600 ? "medium" : "small";

unicorn/no-new-arrayhas suggestions

Disallow new Array() with a single argument

example.js2
const slots = new Array(10);
const list = new Array(input);

unicorn/no-new-bufferhas suggestions

Require Buffer.from() or Buffer.alloc() instead of the deprecated new Buffer()

example.js2
const header = new Buffer(16);
const name = new Buffer("Ada", "utf8");

unicorn/no-nullfixable

Disallow the null literal in favor of undefined

example.js1
let selected = null;

unicorn/no-object-as-default-parameter

Disallow an object literal with properties as the default value of a parameter

example.js1
function connect(options = { retries: 3, secure: true }) {
  return open(options.retries, options.secure);
}

connect({ retries: 5 }); // options.secure is undefined

unicorn/no-process-exit

Disallow process.exit()

example.js1
function loadConfig(path) {
  if (!exists(path)) {
    console.error("config not found");
    process.exit(1);
  }
}

unicorn/no-single-promise-in-promise-methodsfixable

Disallow an array with one item in Promise.all(), Promise.any(), and Promise.race()

example.js3
async function refresh() {
  await Promise.all([saveDraft()]);
  const [user] = await Promise.all([loadUser()]);
  const posts = await Promise.race([loadPosts()]);
  return { user, posts };
}

unicorn/no-static-only-classfixable

Disallow classes that have only static members

example.js1
class Temperature {
  static freezing = 32;

  static toCelsius(fahrenheit) {
    return ((fahrenheit - 32) * 5) / 9;
  }
}

unicorn/no-thenable

Disallow a then property on objects, classes, and module exports

example.js1
const step = {
  then(next) {
    return next;
  },
};

await step; // never finishes: `next` is not called

unicorn/no-this-assignment

Disallow assigning this to a variable

example.js1
class Timer {
  start() {
    const self = this;
    setInterval(function () {
      self.tick();
    }, 1000);
  }
}

unicorn/no-typeof-undefinedfixable

Require comparing with undefined directly instead of typeof x === "undefined"

example.js2
function greet(name, options) {
  if (typeof name === "undefined") {
    name = "stranger";
  }
  return typeof options.loud !== "undefined";
}

unicorn/no-unnecessary-array-splice-countfixable

Disallow .length or Infinity as the count of splice() and toSpliced()

example.js3
queue.splice(3, queue.length);
queue.splice(3, Infinity);

const firstThree = queue.toSpliced(3, Number.POSITIVE_INFINITY);

unicorn/no-unnecessary-awaitfixable

Disallow await on a value that cannot be a promise

example.js3
async function load(ids) {
  const users = await [fetchUser(ids[0]), fetchUser(ids[1])];
  const retries = await 3;
  const total = await (users.length + retries);
  return total;
}

unicorn/no-unnecessary-slice-endfixable

Disallow .length or Infinity as the end of slice()

example.js3
const rest = items.slice(1, items.length);
const extension = filename.slice(dot, Infinity);
const tail = lines.slice(-5, Number.POSITIVE_INFINITY);

unicorn/no-unreadable-array-destructuring

Disallow array destructuring that skips two or more values in a row

example.js2
const [, , , year] = match;

function area([, , width, height]) {
  return width * height;
}

unicorn/no-unreadable-iifehas suggestions

Disallow calling an arrow function in place when its body is in parentheses

example.js1
const label = (() => (count === 1 ? "item" : "items"))();

unicorn/no-useless-collection-argumenthas suggestions

Disallow empty arguments and fallbacks in new Set(), Map, WeakSet, and WeakMap

example.js3
const seen = new Set([]);
const cache = new Map(undefined);

function unique(items) {
  return new Set(items ?? []);
}

unicorn/no-useless-error-capture-stack-tracehas suggestions

Disallow Error.captureStackTrace() in the constructor of an Error subclass

example.js1
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = "ValidationError";
    Error.captureStackTrace(this, ValidationError);
  }
}

unicorn/no-useless-fallback-in-spreadfixable

Disallow an empty object as the fallback when spreading into an object literal

example.js2
function request(url, options, extraHeaders) {
  const headers = { accept: "application/json", ...(extraHeaders || {}) };
  return fetch(url, { ...(options ?? {}), headers });
}

unicorn/no-useless-iterator-to-arrayfixable

Disallow .toArray() on an iterator where the iterator itself works

example.js4
for (const name of users.keys().toArray()) {
  console.log(name);
}

const ids = new Set(users.values().map(user => user.id).toArray());
const list = [...users.values().toArray()];
const hasAdmin = users.values().toArray().some(user => user.admin);

unicorn/no-useless-length-check

Disallow checking an array's length next to .some() or .every() where it changes nothing

example.js3
if (errors.length > 0 && errors.some(error => error.fatal)) {
  stop();
}

const isDone = tasks.length === 0 || tasks.every(task => task.done);
const hasDraft = posts.some(post => post.draft) && posts.length !== 0;

unicorn/no-useless-promise-resolve-rejectfixable

Disallow returning Promise.resolve() or Promise.reject() from an async function or promise callback

example.js3
async function findUser(id) {
  if (!id) {
    return Promise.reject(new Error("missing id"));
  }
  return Promise.resolve(cache.get(id));
}

fetchUser().then(user => Promise.resolve(user.name));

unicorn/no-useless-spreadfixable

Disallow spreads that only build an array or object to take it apart or copy it again

example.js3
const colors = [...["red", "green"], extra];
const config = { ...{ debug: false }, ...overrides };
log(...["saved", file]);

unicorn/no-useless-switch-casehas suggestions

Disallow empty case clauses that fall through to the last default

example.js1
switch (status) {
  case "active":
    show(user);
    break;
  case "banned":
  default:
    hide(user);
}

unicorn/no-useless-undefinedfixable

Disallow undefined where leaving it out means the same

example.js5
let selected = undefined;

function find(id, fallback = undefined) {
  if (!id) {
    return undefined;
  }
  return load(id, undefined);
}

const noop = () => undefined;

unicorn/no-zero-fractionsfixable

Disallow numbers with a zero fraction or a dangling dot

example.js4
const scale = 1.0;
const opacity = 0.50;
const total = 10.;
const limit = 2.0e3;

unicorn/number-literal-casefixable

Require lowercase prefixes and exponents, and uppercase hexadecimal digits, in numbers

example.js5
const mask = 0XFF;
const color = 0xff8800;
const flags = 0B1010;
const mode = 0O755;
const big = 1.5E9;

unicorn/numeric-separators-stylefixable

Enforce how digits are grouped with _ in numbers

example.js4
const population = 1000000;
const price = 12_34_567;
const color = 0xFF8800;
const flags = 0b10101010;

unicorn/prefer-add-event-listener

Require addEventListener() instead of assigning to on properties such as onclick

example.js2
button.onclick = () => save(form);

window.onload = null;

unicorn/prefer-array-find

Require .find() or .findLast() instead of taking one element from .filter()

example.js5
const admin = users.filter(user => user.isAdmin)[0];
const first = users.filter(user => user.isActive).shift();
const [owner] = users.filter(user => user.isOwner);
const newest = users.filter(user => user.isActive).at(-1);
const lastGuest = users.filter(user => user.isGuest).pop();

unicorn/prefer-array-flatfixable

Require .flat() instead of flattening an array with concat, reduce or flatMap

example.js5
const a = pages.flatMap(items => items);
const b = pages.reduce((all, items) => all.concat(items), []);
const c = pages.reduce((all, items) => [...all, ...items], []);
const d = [].concat(...pages);
const e = [].concat.apply([], pages);

unicorn/prefer-array-flat-mapfixable

Require one .flatMap() instead of .map().flat() or .filter().flatMap()

example.js2
const tags = posts.map(post => post.tags).flat();
const words = lines.map(line => line.split(" ")).flat(1);

unicorn/prefer-array-index-of

Require .indexOf() instead of .findIndex() when looking for a value

example.js2
const index = names.findIndex(name => name === "admin");
const last = names.findLastIndex(name => name === current);

unicorn/prefer-array-somehas suggestions

Require .some() to test whether an array has a matching element

example.js4
if (users.find(user => user.isAdmin)) {
  showAdminPanel();
}

const hasAdmin = users.find(user => user.isAdmin) !== undefined;
const hasGuest = users.findIndex(user => user.isGuest) !== -1;
const hasActive = users.filter(user => user.isActive).length > 0;

unicorn/prefer-atfixable

Require .at() to read an element counted from the end of an array or string

example.js7
const last = items[items.length - 1];
const secondToLast = items.slice(-2)[0];
const tail = items.slice(-1).pop();
const newest = _.last(items);
const end = name.charAt(name.length - 1);
const letter = name.substring(index, index + 1);

unicorn/prefer-bigint-literalsfixable

Require BigInt literals such as 123n instead of BigInt(123)

example.js3
const zero = BigInt(0);
const limit = BigInt("9007199254740993");
const mask = BigInt(0xFF);

unicorn/prefer-blob-reading-methods

Require blob.text() and blob.arrayBuffer() instead of a FileReader

example.js1
function readText(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.addEventListener("load", () => resolve(reader.result));
    reader.addEventListener("error", () => reject(reader.error));
    reader.readAsText(file);
  });
}

unicorn/prefer-class-fieldsfixable

Require class fields instead of this assignments of fixed values in the constructor

example.js1
class Counter {
  constructor() {
    this.count = 0;
  }
}

unicorn/prefer-classlist-togglefixable

Require classList.toggle() instead of choosing between add() and remove()

example.js3
if (isOpen) {
  menu.classList.add("open");
} else {
  menu.classList.remove("open");
}

isOpen ? menu.classList.add("open") : menu.classList.remove("open");

menu.classList[isOpen ? "add" : "remove"]("open");

unicorn/prefer-code-pointfixable

Require codePointAt() and String.fromCodePoint() over charCodeAt() and String.fromCharCode()

example.js2
const code = text.charCodeAt(0);
const letter = String.fromCharCode(code);

unicorn/prefer-date-nowfixable

Require Date.now() to get the current time in milliseconds

example.js3
const start = new Date().getTime();
const stamp = +new Date();
const elapsed = new Date() - start;

unicorn/prefer-default-parametershas suggestions

Require default parameters instead of reassigning a parameter in the function body

example.js1
function greet(name) {
  name = name || "stranger";
  return `Hello, ${name}`;
}

unicorn/prefer-dom-node-appendfixable

Require .append() instead of .appendChild() on DOM nodes

example.js1
const item = document.createElement("li");
list.appendChild(item);

unicorn/prefer-dom-node-datasetfixable

Require .dataset instead of the attribute methods for data-* attributes

example.js4
element.setAttribute("data-user-id", user.id);
const id = element.getAttribute("data-user-id");

if (element.hasAttribute("data-user-id")) {
  element.removeAttribute("data-user-id");
}

unicorn/prefer-dom-node-remove

Require child.remove() instead of parent.removeChild(child)

example.js2
item.parentNode.removeChild(item);
list.removeChild(list.firstChild);

unicorn/prefer-dom-node-text-contentfixable

Require .textContent instead of .innerText

example.js3
const title = heading.innerText;
status.innerText = "Saved";
const { innerText } = heading;

unicorn/prefer-event-target

Require EventTarget instead of EventEmitter

example.js2
import { EventEmitter } from "node:events";

class Download extends EventEmitter {}

const bus = new EventEmitter();

unicorn/prefer-export-fromhas suggestions

Require export ... from to re-export instead of an import and a separate export

example.js1
import { parse } from "./parser.js";

export { parse };

unicorn/prefer-global-thishas suggestions

Require globalThis instead of window, self and global

example.js3
global.cache = new Map();
window.setTimeout(refresh, 1000);

if (typeof window !== "undefined") {
  start();
}

unicorn/prefer-import-meta-propertiesfixable

Require import.meta.dirname and import.meta.filename instead of building them from the URL

example.js2
import path from "node:path";
import { fileURLToPath } from "node:url";

const filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(filename);

unicorn/prefer-includeshas suggestions

Require .includes() instead of comparing .indexOf() with -1 or 0

example.js2
if (tags.indexOf("draft") !== -1) {
  hide(post);
}

if (email.indexOf("@") < 0) {
  reject(email);
}

unicorn/prefer-keyboard-event-keyfixable

Require event.key instead of keyCode, charCode and which

example.js1
input.addEventListener("keydown", event => {
  if (event.keyCode === 13) {
    submit();
  }
});

unicorn/prefer-logical-operator-over-ternaryhas suggestions

Require || or ?? instead of a ternary that repeats its condition

example.js2
const name = user.name ? user.name : "Anonymous";
const port = !config.port ? 3000 : config.port;

unicorn/prefer-math-min-maxhas suggestions

Require Math.min() and Math.max() instead of a ternary that picks the smaller or larger value

example.js2
const width = size > 100 ? 100 : size;
const volume = level < 0 ? 0 : level;

unicorn/prefer-math-trunchas suggestions

Require Math.trunc() instead of bitwise tricks to drop the fraction of a number

example.js3
const seconds = (elapsed / 1000) | 0;
const page = ~~(index / pageSize);
total >>= 0;

unicorn/prefer-modern-dom-apishas suggestions

Require .before(), .after(), .append(), .prepend() and .replaceWith() over older DOM methods

example.js4
list.insertBefore(item, marker);
list.replaceChild(item, placeholder);
heading.insertAdjacentText("beforeend", "!");
heading.insertAdjacentElement("afterend", note);

unicorn/prefer-modern-math-apis

Require Math.log10(), Math.log2(), Math.hypot() and Math.abs() over hand-written formulas

example.js4
const digits = Math.log(value) * Math.LOG10E;
const bits = Math.log(value) / Math.LN2;
const distance = Math.sqrt(dx * dx + dy ** 2);
const size = Math.sqrt(delta * delta);

unicorn/prefer-module

Require ES modules instead of CommonJS

example.js4
"use strict";

const path = require("path");

module.exports = path.join(__dirname, "config.json");

unicorn/prefer-native-coercion-functions

Require passing String, Number, BigInt, Boolean and Symbol directly instead of wrapping them

example.js3
const ids = rows.map(id => Number(id));

const names = users.map(user => user.name).filter(name => name);

function toText(value) {
  return String(value);
}

unicorn/prefer-negative-indexfixable

Require a negative index instead of .length - index

example.js4
const lastTwo = items.slice(items.length - 2);
const last = items.at(items.length - 1);
queue.splice(queue.length - 1, 1);
const rest = Array.prototype.slice.call(list, list.length - 2);

unicorn/prefer-node-protocolfixable

Require the node: prefix when importing Node.js built-in modules

example.js4
import fs from "fs";
import { readFile } from "fs/promises";
export { EventEmitter } from "events";

const os = await import("os");

unicorn/prefer-number-coercionhas suggestions

Require Number() instead of parseFloat() and base-10 parseInt()

example.js2
const price = parseFloat(input.value);
const quantity = Number.parseInt(form.quantity, 10);

unicorn/prefer-number-propertiesfixable

Require Number.isNaN, Number.parseInt and the like instead of the globals

example.js4
const width = parseInt(style.width, 10);

if (isNaN(width)) {
  reset(NaN);
}

const isValid = window.isFinite(width);

unicorn/prefer-object-from-entries

Require Object.fromEntries() to turn key-value pairs into an object

example.js3
const byId = users.reduce((result, user) => ({ ...result, [user.id]: user }), {});

const ages = users.reduce((result, user) => Object.assign(result, { [user.name]: user.age }), {});

const query = _.fromPairs(pairs);

unicorn/prefer-optional-catch-bindingfixable

Require catch {} without a parameter when the error is not used

example.js2
function parse(text) {
  try {
    return JSON.parse(text);
  } catch (error) {
    return undefined;
  }
}

try {
  await upload(file);
} catch ({ message }) {
  retry(file);
}

unicorn/prefer-prototype-methodsfixable

Require borrowing a method from the prototype instead of from an empty [] or {}

example.js3
const links = [].slice.call(document.links);
const isOwn = {}.hasOwnProperty.call(user, "name");
const first = Reflect.apply([].at, items, [0]);

unicorn/prefer-query-selectorfixable

Require querySelector() and querySelectorAll() instead of the getElement... methods

example.js4
const menu = document.getElementById("menu");
const items = menu.getElementsByClassName("item active");
const links = menu.getElementsByTagName("a");
const emails = document.getElementsByName("email");

unicorn/prefer-reflect-applyhas suggestions

Require Reflect.apply() instead of Function.prototype.apply()

example.js2
function logged() {
  console.log("called");
  return handler.apply(this, arguments);
}

Function.prototype.apply.call(handler, null, [event]);

unicorn/prefer-regexp-testfixable

Require RegExp#test() instead of String#match() and RegExp#exec() for a yes or no answer

example.js3
if (email.match(/@/)) {
  send(email);
}

const isHex = Boolean(/^[0-9a-f]+$/i.exec(color));

const label = !name.match(/\S/) ? "(empty)" : name;

unicorn/prefer-response-static-jsonhas suggestions

Require Response.json() instead of new Response(JSON.stringify())

example.js2
Bun.serve({
  routes: {
    "/user": () => new Response(JSON.stringify({ name: "Ada" })),
    "/missing": () => new Response(JSON.stringify({ error: "not found" }), { status: 404 }),
  },
});

unicorn/prefer-set-hasfixable

Require Set#has() instead of Array#includes() for an array that is only searched

example.js1
const blocked = ["admin", "root", "system"];

function isAllowed(name) {
  return !blocked.includes(name);
}

unicorn/prefer-set-sizefixable

Require Set#size instead of converting the Set to an array to read length

example.js2
const uniqueCount = [...new Set(tags)].length;

const visitors = new Set(ids);
console.log(Array.from(visitors).length);

unicorn/prefer-single-callfixable

Require one call instead of several in a row to push(), unshift(), classList.add() and the like

example.js4
errors.push("name is required");
errors.push("email is not valid");

queue.unshift(second);
queue.unshift(first);

button.classList.add("active");
button.classList.add("large");

importScripts("parser.js");
importScripts("worker-utils.js");

unicorn/prefer-spreadfixable

Require the spread operator instead of Array.from(), concat(), slice() and toSpliced()

example.js4
const links = Array.from(document.querySelectorAll("a"));
const copy = items.slice();
const sorted = scores.toSpliced().sort();
const all = items.concat(more, last);

unicorn/prefer-string-rawfixable

Require String.raw for strings that escape backslashes

example.js2
const logs = "C:\\Users\\ada\\logs";
const version = new RegExp("\\d+\\.\\d+");

unicorn/prefer-string-replace-allfixable

Require replaceAll() instead of replace() with a global regex

example.js2
const slug = title.replace(/\s+/g, "-");
const path = name.replaceAll(/\./g, "/");

unicorn/prefer-string-slicefixable

Require slice() instead of substr() and substring()

example.js2
const year = date.substring(0, 4);
const month = date.substr(5, 2); // 2 is a length

unicorn/prefer-string-starts-ends-withfixable

Require startsWith() and endsWith() instead of a regex with ^ or $

example.js2
const secure = /^https:/.test(url);
const isScript = /\.js$/.test(file);

unicorn/prefer-string-trim-start-endfixable

Require trimStart() and trimEnd() instead of trimLeft() and trimRight()

example.js2
const command = input.trimLeft();
const line = text.trimRight();

unicorn/prefer-structured-clonehas suggestions

Require structuredClone() for deep clones

example.js2
const draft = JSON.parse(JSON.stringify(settings));
const backup = _.cloneDeep(settings);

unicorn/prefer-ternary

Require a ternary instead of an if/else where both branches do the same kind of thing

example.js2
function label(user) {
  if (user.isAdmin) {
    return "admin";
  } else {
    return "member";
  }
}

let timeout;
if (retrying) {
  timeout = 5000;
} else {
  timeout = 1000;
}

unicorn/prefer-top-level-await

Require top-level await instead of promise chains and async wrappers at the top of a module

example.js3
(async () => {
  await connect();
})();

loadConfig().then(config => start(config));

async function main() {
  await migrate();
}
main();

unicorn/prefer-type-errorfixable

Require TypeError when the throw follows a type check

example.js2
function repeat(text, times) {
  if (typeof text !== "string") {
    throw new Error("text must be a string");
  }
  if (!Number.isInteger(times)) {
    throw new Error("times must be an integer");
  }
  return text.repeat(times);
}

unicorn/relative-url-stylefixable

Enforce one style for relative URLs in new URL(): with or without ./

example.js2
const logo = new URL("./logo.png", import.meta.url);
const icon = new URL(`./${name}.svg`, import.meta.url);

unicorn/require-array-join-separatorfixable

Require the separator argument of Array#join()

example.js2
const header = columns.join();
const names = Array.prototype.join.call(arguments);

unicorn/require-module-attributeshas suggestions

Disallow an empty attribute list in import, export and import()

example.js4
import settings from "./settings.json" with {};
export { version } from "./package.json" with {};

const en = await import("./en.json", {});
const de = await import("./de.json", { with: {} });

unicorn/require-module-specifiersfixable

Disallow empty braces in import and export statements

example.js3
import {} from "./setup.js";
import app, {} from "./app.js";
export {};

unicorn/require-post-message-target-originhas suggestions

Require the targetOrigin argument of window.postMessage()

example.js2
window.parent.postMessage({ type: "ready" });
frame.contentWindow.postMessage(token);

unicorn/switch-case-bracesfixable

Enforce braces, or no braces, around the body of each case

example.js2
switch (event.type) {
  case "open":
    show(event.target);
    break;
  default:
    ignore(event);
}

unicorn/switch-case-break-position

Require break, return, continue and throw inside the braces of a case

example.js1
switch (event.type) {
  case "open": {
    show(event.target);
  }
  break;
}

unicorn/text-encoding-identifier-casefixable

Enforce one spelling for the encoding names utf8 and ascii

example.js3
const text = await fs.readFile(file, "UTF-8");
const bytes = Buffer.from(name, "ASCII");
const decoder = new TextDecoder("utf8");

unicorn/throw-new-errorfixable

Require new when creating an error

example.js3
throw Error("Not found");
throw errors.ValidationError("Name is required");
const timeout = TypeError("Expected a number");

promise 16 rules

promise/always-return

Require every then() callback to return a value or throw

example.js1
fetchUser(id)
  .then(user => {
    user.lastSeen = Date.now();
  })
  .then(user => render(user)); // user is undefined

promise/avoid-new

Disallow creating promises with new Promise()

example.js1
import fs from "node:fs";

function readConfig(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, "utf8", (err, text) => {
      if (err) reject(err);
      else resolve(text);
    });
  });
}

promise/catch-or-return

Require a promise chain to end with catch() or to be returned

example.js1
function refresh() {
  loadItems().then(render);
}

promise/no-callback-in-promise

Disallow calling a callback inside then() or catch()

example.js2
function getUser(id, callback) {
  fetchUser(id)
    .then(user => callback(null, user))
    .catch(callback);
}

promise/no-multiple-resolved

Disallow paths that call resolve or reject more than once in a new Promise() executor

example.js1
function readConfig(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, "utf8", (err, text) => {
      if (err) {
        reject(err);
      }
      resolve(text); // also runs after reject(err)
    });
  });
}

promise/no-nesting

Disallow then() and catch() nested inside another then() or catch()

example.js1
getUser(id).then(user => {
  return getOrders(user).then(orders => render(orders));
});

promise/no-new-staticsfixable

Disallow new on static Promise methods such as Promise.all

example.js1
async function loadPage() {
  return await new Promise.all([loadUser(), loadOrders()]);
}

promise/no-promise-in-callback

Disallow promises inside error-first callbacks

example.js1
fs.readFile(path, "utf8", (err, text) => {
  if (err) throw err;
  upload(text).then(notify);
});

promise/no-return-in-finally

Disallow return in a finally() callback

example.js1
loadItems()
  .finally(() => {
    hideSpinner();
    return "done";
  })
  .then(result => console.log(result)); // the items, not "done"

promise/no-return-wrap

Disallow returning Promise.resolve() or Promise.reject() from a promise callback

example.js2
fetchUser(id).then(user => {
  if (!user.active) {
    return Promise.reject(new Error("inactive user"));
  }
  return Promise.resolve(user.profile);
});

promise/param-names

Require the parameters of a new Promise() executor to be named resolve and reject

example.js2
new Promise((reject, resolve) => {
  setTimeout(resolve, 1000); // rejects the promise
});

promise/prefer-await-to-callbacks

Require async/await instead of error-first callbacks

example.js4
function readConfig(path, callback) {
  fs.readFile(path, "utf8", (err, text) => {
    if (err) return callback(err);
    callback(null, JSON.parse(text));
  });
}

promise/prefer-await-to-then

Require await instead of then(), catch() and finally()

example.js2
function showUser(id) {
  fetchUser(id)
    .then(user => render(user))
    .catch(err => showError(err));
}

promise/prefer-catch

Require catch() instead of a second argument to then()

example.js1
loadItems().then(render, showError); // an error in render is not handled

promise/spec-only

Disallow Promise static methods that are not part of the language

example.js1
const pages = await Promise.map(urls, url => fetch(url));

promise/valid-params

Require the right number of arguments for Promise methods

example.js1
const results = Promise.all(loadUser(), loadOrders());

Node.js (n) 15 rules

n/callback-return

Require a return with every call to a callback

example.js1
function loadUser(id, callback) {
  if (!id) {
    callback(new Error("missing id"));
  }
  db.find(id, callback);
}

n/exports-style

Enforce either module.exports or exports in CommonJS files

example.js1
exports.parse = function parse(text) {
  return JSON.parse(text);
};

n/global-require

Require require() calls to be at the top level of the file

example.js2
function readConfig(file) {
  const fs = require("fs");
  return fs.readFileSync(file, "utf8");
}

if (process.env.DEBUG) {
  require("./debug");
}

n/handle-callback-err

Require callbacks to use their error parameter

example.js1
fs.readFile("config.json", (err, data) => {
  start(JSON.parse(data));
});

n/no-exports-assignfixable

Disallow assigning to exports

example.js1
exports = {
  parse,
  stringify,
};

n/no-mixed-requires

Disallow require() calls mixed with other variables in one declaration

example.js1
const fs = require("fs"),
  retries = 3;

n/no-new-require

Disallow new require()

example.js1
const app = new require("./app");

n/no-path-concat

Disallow joining __dirname and __filename to a path with + or a template

example.js3
const config = __dirname + "/config.json";
const cache = `${__dirname}/cache/${name}`;
const logs = __dirname + path.sep + "logs";

n/no-process-env

Disallow process.env

example.js1
if (process.env.NODE_ENV === "development") {
  enableLogging();
}

n/no-process-exit

Disallow process.exit()

example.js1
if (!config.valid) {
  console.error("invalid config");
  process.exit(1);
}

n/no-sync

Disallow calls to functions with names that end in Sync

example.js1
function handle(request) {
  const page = fs.readFileSync("index.html", "utf8");
  return new Response(page);
}

n/no-top-level-await

Disallow await at the top level of a module

example.js2
import { connect } from "./db.js";

export const db = await connect();

for await (const row of db.rows()) {
  console.log(row);
}

n/no-unsupported-features/es-syntax

Disallow JavaScript syntax that the Node.js versions you support do not have

example.js2
class Cache {
  static instance;
  static {
    Cache.instance = new Cache();
  }
}

const word = /[\p{L}--[a-z]]/v;

n/no-unsupported-features/node-builtins

Disallow Node.js APIs that the Node.js versions you support do not have

example.js3
import { parseArgs } from "node:util";

const { values } = parseArgs({ options: { url: { type: "string" } } });
const response = await fetch(values.url);
console.log(import.meta.dirname);

oxc 27 rules

oxc/approx-constanthas suggestions

Disallow numbers that approximate a constant of the Math object

example.js2
function circleArea(radius) {
  return 3.14159 * radius * radius;
}

const diagonal = side * 1.4142;

oxc/bad-array-method-on-arguments

Disallow calling array methods on the arguments object

example.js1
function total() {
  return arguments.reduce((sum, price) => sum + price, 0);
}

oxc/bad-bitwise-operatorhas suggestions

Disallow & and | where && and || were probably meant

example.js3
const city = user & user.city;
const label = title | "Untitled";
options |= {};

oxc/bad-char-at-comparison

Disallow comparing a single character with a longer string

example.js2
if (path.charAt(0) === "./") {
  resolveRelative(path);
}

const prefix = "https://example.com";
if (prefix[0] === "ht") {
  useTls();
}

oxc/bad-comparison-sequence

Disallow chained comparisons such as a < b < c

example.js2
if (min < value < max) {
  accept(value);
}

const allEqual = width === height === depth;

oxc/bad-match-all-arg

Require the g flag on a regular expression passed to matchAll()

example.js2
const words = [...text.matchAll(/\w+/)];

const tag = new RegExp("#\\w+", "i");
for (const match of text.matchAll(tag)) {
  tags.push(match[0]);
}

oxc/bad-min-max-func

Disallow Math.min and Math.max combinations that always give the same result

example.js2
const percent = Math.min(Math.max(input, 100), 0);
const channel = Math.max(255, Math.min(0, red));

oxc/bad-object-literal-comparison

Disallow comparing a value with {} or []

example.js2
if (items === []) {
  showPlaceholder();
}

if (filters !== {}) {
  applyFilters(filters);
}

oxc/bad-replace-all-arg

Require the g flag on a regular expression passed to replaceAll()

example.js2
const slug = title.replaceAll(/\s+/, "-");

const digits = new RegExp("\\d");
const masked = cardNumber.replaceAll(digits, "*");

oxc/branches-sharing-codehas suggestions

Disallow the same statements at the start or end of every branch of an if

example.js1
if (user.isAdmin) {
  showAdminMenu();
  render();
} else {
  showMenu();
  render();
}

oxc/const-comparisons

Disallow comparisons that are always true, always false, or repeated

example.js3
if (age > 65 && age < 18) {
  addDiscount();
}

if (status >= 400 && status >= 500) {
  retry();
}

while (index < index) {
  index++;
}

oxc/double-comparisonshas suggestions

Disallow two comparisons of the same values that one operator can express

example.js2
if (stock === minimum || stock < minimum) {
  reorder();
}

const changed = before < after || before > after;

oxc/erasing-opfixable

Disallow operations with 0 that erase the other operand

example.js3
const tax = price * 0;
const masked = flags & 0;
const ratio = 0 / total;

oxc/misrefactored-assign-ophas suggestions

Disallow a += a + b, where the target is repeated on the right

example.js3
total += total + price;
scale *= 2 * scale;
cart.remaining -= cart.remaining - used;

oxc/missing-throwhas suggestions

Require throw before a new Error() that is not used

example.js1
function setAge(age) {
  if (age < 0) {
    new Error("age must not be negative");
  }
  user.age = age;
}

oxc/no-accumulating-spread

Disallow spreading the accumulator in reduce() and in loops

example.js2
const names = users.reduce((result, user) => [...result, user.name], []);

const byId = users.reduce((result, user) => ({ ...result, [user.id]: user }), {});

oxc/no-async-await

Disallow async functions

example.js3
async function loadUser(id) {
  const response = await fetch(`/users/${id}`);
  return response.json();
}

const refresh = async () => {
  await loadUser(current.id);
};

class Store {
  async save() {}
}

oxc/no-async-endpoint-handlers

Disallow async functions as Express route handlers and middleware

example.js1
app.get("/users/:id", async (req, res) => {
  const user = await db.findUser(req.params.id);
  res.json(user);
});

oxc/no-barrel-file

Disallow files that load too many modules through export *

index.js1
export * from "./users.js";
export * from "./orders.js";

oxc/no-const-enumfixable

Disallow TypeScript const enum

example.ts1
export const enum Status {
  Active,
  Archived,
}

oxc/no-map-spreadhas suggestions

Disallow object and array spreads in the return value of map and flatMap callbacks

example.js1
function loadTasks() {
  const tasks = readTasks();
  return tasks.map(task => ({ ...task, done: false }));
}

oxc/number-arg-out-of-range

Disallow out-of-range arguments to toString, toFixed, toExponential, and toPrecision

example.js1
const binary = count.toString(1);

oxc/only-used-in-recursionfixable

Disallow parameters that are only passed on to the function's own recursive calls

example.js1
function countNodes(node, depth) {
  if (!node) return 0;
  return 1 + countNodes(node.left, depth) + countNodes(node.right, depth);
}

oxc/uninvoked-array-callback

Disallow array methods with a callback directly on new Array(n)

example.js1
const scores = new Array(5).map(() => 0);

Bun 29 rules

bun/consistent-directive-prefixfixable

Require comments that turn rules off to start with the same word, eslint or oxlint

example.js1
// oxlint-disable-next-line no-console
console.log(version);

bun/formatfixable

Require code to be formatted the way bun fmt prints it

example.js1
const user = {name:'Ada'}

bun/jsx-no-forbidden-nesting

Disallow a JSX element inside another element that cannot contain it

example.jsx1
const card = (
  <a href={post.url}>
    <h2>{post.title}</h2>
    <button onClick={save}>Save</button>
  </a>
);

bun/no-array-just-to-countfixable

Disallow .split(separator).length with a one-character separator

example.js1
const lineCount = source.split("\n").length;

bun/no-cast-to-brand

Disallow casts to a branded type outside the module that declares it

src/routes.ts1
import type { UserId } from "./ids";

export function userOf(params: { id: string }) {
  return load(params.id as UserId);
}

bun/no-console-window-flash

Require windowsHide for a child process that does not share a stream with its parent

example.js1
import { execFile } from "node:child_process";

execFile("git", ["status", "--short"], (error, stdout) => {
  show(stdout);
});

bun/no-cwd-dependent-path

Disallow relative paths that depend on the directory the process was started in

example.js1
import { readFileSync } from "node:fs";

const config = JSON.parse(readFileSync("config.json", "utf8"));

bun/no-eager-dynamic-import

Disallow import() that runs as soon as the module is loaded

example.js1
const { marked } = await import("marked");

export function render(text) {
  return marked.parse(text);
}

bun/no-eager-native-addon

Disallow loading a .node native addon as soon as the module is loaded

example.js1
const native = require("./build/Release/resize.node");

export function resize(image, width) {
  return native.resize(image, width);
}

bun/no-env-at-module-scope

Disallow reading environment variables as soon as the module is loaded

example.js1
const apiUrl = process.env.API_URL;

export function fetchUser(id) {
  return fetch(`${apiUrl}/users/${id}`);
}

bun/no-literal-temp-dir

Disallow hard-coded temporary directories such as /tmp in favor of os.tmpdir()

example.js2
import { writeFileSync } from "node:fs";

writeFileSync("/tmp/report.json", JSON.stringify(report));
writeFileSync(`/var/tmp/${name}.log`, lines.join("\n"));

bun/no-negative-repeat-counthas suggestions

Disallow a repeat() count that comes from a subtraction and can be negative

example.js1
function pad(label, width) {
  return label + " ".repeat(width - label.length);
}

bun/no-redundant-catch-typefixable

Disallow : unknown on the variable of a catch clause

example.ts1
try {
  await save(draft);
} catch (error: unknown) {
  report(error);
}

bun/no-restricted-text

Disallow text that matches a pattern in strings, templates, JSX text, comments, and regular expressions

example.js2
const api = "http://api.example.com";
const avatar = `http://img.example.com/${user.id}.png`;

bun/no-side-effects-on-import

Disallow top-level statements that call something or assign to a property

example.js1
import { createServer } from "./server.js";

createServer().listen(3000);

bun/no-unbounded-requests

Require an axios request to have a timeout or a signal

example.js3
import axios from "axios";

const user = await axios.get(`/users/${id}`);
await axios.post("/users", user, { headers });
const api = axios.create({ baseURL: "https://api.example.com", timeout: 0 });

bun/no-unhandled-emitter-errors

Require an error listener on event emitters that Node.js functions return

example.js1
import { spawn } from "node:child_process";

const child = spawn("git", ["status", "--short"]);
child.stdout.on("data", show);
child.on("exit", done);

bun/no-unportable-commands

Disallow starting a process with a command that does not work the same on every system

example.js3
import { exec, spawn } from "node:child_process";

spawn("npm", ["install"]);
spawn("rm", ["-rf", "dist"]);
exec("bun run build && bun run deploy");

bun/no-unseeded-random-in-property-test

Disallow Math.random() and fc.sample() inside a fast-check property

example.ts1
import fc from "fast-check";
import { pad } from "./text";

fc.assert(
  fc.property(fc.string(), text => {
    const width = Math.floor(Math.random() * 80);
    return pad(text, width).length >= width;
  }),
);

bun/no-useless-spread-fallbackhas suggestions

Disallow an empty object as the fallback in ...(condition ? value : {})

example.js1
const headers = {
  accept: "application/json",
  ...(apiKey ? { "x-api-key": apiKey } : {}),
};

bun/prefer-builtin-sleephas suggestions

Prefer Bun.sleep() to a promise that only waits for setTimeout

example.js1
await new Promise(resolve => setTimeout(resolve, 500));

bun/prefer-lazy-schema

Disallow building a validation schema while the module loads

example.ts1
import { z } from "zod";

export const User = z.object({
  name: z.string().min(1),
  email: z.string().email(),
});

bun/prefer-local-modulefixable

Require importing a package through the module in your project that wraps it

example.ts1
import axios from "axios";

const users = await axios.get("/users");

bun/prefer-timeout-signal

Prefer AbortSignal.timeout() to an AbortController that only a timer aborts

example.js1
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);

const response = await fetch(url, { signal: controller.signal });

bun/prefer-url-for-sibling-files

Prefer new URL("./file", import.meta.url) to a path joined onto the module's directory

example.js1
import { join } from "node:path";

const schema = await Bun.file(join(import.meta.dirname, "schema.sql")).text();

bun/react-no-latest-value-ref

Disallow a ref that is set to the latest value on every render

example.jsx1
import { useRef } from "react";

function Chat({ onMessage }) {
  const onMessageRef = useRef(onMessage);
  onMessageRef.current = onMessage;
  // ...
}

bun/react-no-local-render-helpers

Disallow renderItem() functions that are defined inside a component and used in its JSX

example.jsx1
function UserList({ users }) {
  const renderUser = user => <li key={user.id}>{user.name}</li>;

  return <ul>{users.map(renderUser)}</ul>;
}

bun/react-no-namespace-hooksfixable

Require React hooks to be imported by name: useState(), not React.useState()

example.jsx2
import React from "react";

function Counter() {
  const [count, setCount] = React.useState(0);
  React.useEffect(() => {
    document.title = `${count} clicks`;
  }, [count]);

  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

bun/react-prefer-updater-function

Prefer setItems(previous => ..) to a new array or object made from the state of the last render

example.jsx1
import { useState } from "react";

function Cart() {
  const [items, setItems] = useState([]);

  async function add(id) {
    const item = await loadItem(id);
    setItems([...items, item]);
  }
  // ...
}

bun:test 65 rules

bun:test/consistent-test-filename

Require test files to follow one naming pattern

src/user.spec.ts1
import { expect, test } from "bun:test";

test("has a name", () => {
  expect(loadUser().name).toBe("Ada");
});

bun:test/consistent-test-itfixable

Enforce one of test or it for defining tests

example.ts2
import { describe, expect, it, test } from "bun:test";

it("adds", () => {
  expect(1 + 1).toBe(2);
});

describe("subtract", () => {
  test("goes below zero", () => {
    expect(1 - 2).toBe(-1);
  });
});

bun:test/expect-expect

Require every test to make at least one assertion

example.ts1
import { test } from "bun:test";

test("parses the port", () => {
  parseConfig("port = 3000");
});

bun:test/max-expects

Enforce a maximum number of expect() calls in a test

example.ts1
import { expect, test } from "bun:test";

test("creates a user", () => {
  const user = createUser({ name: "Ada", email: "ada@example.com" });
  expect(user.id).toBeGreaterThan(0);
  expect(user.name).toBe("Ada");
  expect(user.email).toBe("ada@example.com");
  expect(user.role).toBe("member");
  expect(user.active).toBe(true);
  expect(user.createdAt).toBeInstanceOf(Date);
});

bun:test/max-nested-describe

Enforce a maximum depth of nested describe() calls

example.ts1
import { describe, expect, test } from "bun:test";

describe("cart", () => {
  describe("checkout", () => {
    describe("with a coupon", () => {
      test("lowers the total", () => {
        expect(checkout({ coupon: "SAVE10" }).total).toBe(90);
      });
    });
  });
});

bun:test/no-alias-methodsfixable

Disallow matcher aliases such as toBeCalled in favor of toHaveBeenCalled

example.ts1
import { expect, mock, test } from "bun:test";

test("saves the draft", () => {
  const save = mock();
  save("draft");
  expect(save).toBeCalledWith("draft");
});

bun:test/no-commented-out-tests

Disallow tests that are inside a comment

example.ts1
import { expect, test } from "bun:test";

// test("rounds down", () => {
//   expect(Math.floor(1.9)).toBe(1);
// });

bun:test/no-conditional-expect

Disallow expect inside if, catch and other code that only runs sometimes

example.ts1
import { expect, test } from "bun:test";

test("rejects bad JSON", () => {
  try {
    JSON.parse("{");
  } catch (error) {
    expect(error).toBeInstanceOf(SyntaxError);
  }
});

bun:test/no-conditional-in-test

Disallow if, switch, ? :, &&, || and ?? inside a test

example.ts1
import { expect, test } from "bun:test";
import { label, users } from "./users";

test("labels every user", () => {
  for (const user of users) {
    const expected = user.admin ? "Admin" : "Member";
    expect(label(user)).toBe(expected);
  }
});

bun:test/no-conditional-tests

Disallow test and describe inside an if statement

example.ts1
import { expect, test } from "bun:test";

if (process.platform === "linux") {
  test("reads the kernel version", async () => {
    expect(await Bun.file("/proc/version").text()).toContain("Linux");
  });
}

bun:test/no-disabled-testson by default

Disallow skipped tests such as test.skip, xtest and xdescribe

example.ts2
import { describe, expect, test, xtest } from "bun:test";

describe.skip("checkout", () => {
  test("adds the tax", () => {
    expect(total(100)).toBe(108);
  });
});

xtest("applies the coupon", () => {
  expect(total(100, "SAVE10")).toBe(97.2);
});

bun:test/no-done-callback

Disallow the done callback in tests and hooks in favor of promises

example.ts1
import { expect, test } from "bun:test";

test("reads the config", done => {
  readConfig((error, config) => {
    expect(config.port).toBe(3000);
    done();
  });
});

bun:test/no-duplicate-hooks

Disallow using the same hook twice in one describe block

example.ts1
import { beforeEach, describe, test } from "bun:test";

describe("cart", () => {
  beforeEach(() => {
    cart.clear();
  });

  test("starts empty", () => {});

  beforeEach(() => {
    cart.add("apple");
  });
});

bun:test/no-exporton by default

Disallow exports from a file that has tests

src/cart.test.ts1
import { expect, test } from "bun:test";

export function makeCart() {
  return { items: [] };
}

test("starts empty", () => {
  expect(makeCart().items).toEqual([]);
});

bun:test/no-focused-testson by defaulthas suggestions

Disallow test.only and describe.only

example.ts1
import { expect, test } from "bun:test";

test.only("adds", () => {
  expect(1 + 1).toBe(2);
});

bun:test/no-hooks

Disallow beforeAll, beforeEach, afterEach and afterAll

example.ts1
import { beforeEach, expect, test } from "bun:test";

let cart;

beforeEach(() => {
  cart = createCart();
});

test("starts empty", () => {
  expect(cart.items).toEqual([]);
});

bun:test/no-identical-title

Disallow two tests or two describe blocks with the same title in one block

example.ts1
import { describe, expect, test } from "bun:test";

describe("parsePrice", () => {
  test("reads a number", () => {
    expect(parsePrice("12")).toBe(12);
  });

  test("reads a number", () => {
    expect(parsePrice("12.50")).toBe(12.5);
  });
});

bun:test/no-interpolation-in-snapshots

Disallow ${} in inline snapshots

example.ts1
import { expect, test } from "bun:test";

test("creates a user", () => {
  const user = createUser("Ada");
  expect(user).toMatchInlineSnapshot(`
    {
      "id": ${user.id},
      "name": "Ada",
    }
  `);
});

bun:test/no-large-snapshots

Disallow snapshots that are longer than a set number of lines

example.ts1
import { expect, test } from "bun:test";

test("loads the user", () => {
  expect(loadUser(1)).toMatchInlineSnapshot(`
    {
      "email": "ada@example.com",
      "id": 1,
      "name": "Ada",
    }
  `);
});

bun:test/no-restricted-jest-methods

Disallow the jest and vi methods that you list

example.ts2
import { expect, jest, test } from "bun:test";

test("retries after a second", () => {
  const send = jest.spyOn(mailer, "send");
  retry(send);
  jest.advanceTimersByTime(1000);
  expect(send).toHaveBeenCalledTimes(2);
});

bun:test/no-restricted-matchers

Disallow the matchers and modifiers you list

example.ts1
import { expect, test } from "bun:test";

test("a new user is active", () => {
  const user = { active: true };
  expect(user.active).toBeTruthy();
});

bun:test/no-standalone-expect

Disallow expect outside of a test or it block

example.ts2
import { beforeEach, describe, expect } from "bun:test";

describe("cart", () => {
  const cart = createCart();
  expect(cart.items).toHaveLength(0);

  beforeEach(() => {
    expect(cart.total).toBe(0);
  });
});

bun:test/no-test-prefixesfixable

Require .skip instead of the x prefix

example.ts2
import { expect, xdescribe, xtest } from "bun:test";

xtest("subtracts", () => {
  expect(3 - 1).toBe(2);
});

xdescribe("division", () => {});

bun:test/no-test-return-statement

Disallow return expect(..) in a test

example.ts1
import { expect, test } from "bun:test";

test("adds the prices", () => {
  const total = sum([1, 2]);
  return expect(total).toBe(3);
});

bun:test/no-unneeded-async-expect-functionfixable

Disallow an async function around a single awaited call in expect

example.ts2
import { expect, test } from "bun:test";

test("rejects an empty name", async () => {
  await expect(async () => {
    await saveUser({ name: "" });
  }).rejects.toThrow("name is required");

  await expect(async () => await saveUser({ name: "" })).rejects.toThrow();
});

bun:test/padding-around-after-all-blocksfixable

Require a blank line before each afterAll call

example.ts1
import { afterAll, describe } from "bun:test";

describe("database", () => {
  const db = connect();
  afterAll(() => db.close());
});

bun:test/padding-around-test-blocksfixable

Require a blank line before each test and it call

example.ts2
import { describe, expect, test } from "bun:test";

describe("cart", () => {
  const cart = createCart();
  test("starts empty", () => {
    expect(cart.items).toEqual([]);
  });
  test.skip("adds an item", () => {
    expect(cart.add("apple").items).toEqual(["apple"]);
  });
});

bun:test/prefer-called-oncefixable

Require toHaveBeenCalledOnce() instead of toHaveBeenCalledTimes(1)

example.ts1
import { expect, mock, test } from "bun:test";

test("notifies the listener", () => {
  const listener = mock();
  publish("ready", listener);
  expect(listener).toHaveBeenCalledTimes(1);
});

bun:test/prefer-called-timesfixable

Require toHaveBeenCalledTimes(1) instead of toHaveBeenCalledOnce()

example.ts1
import { expect, mock, test } from "bun:test";

test("does not retry after a success", () => {
  const request = mock();
  retryFetch(request);
  expect(request).toHaveBeenCalledOnce();
});

bun:test/prefer-called-withhas suggestions

Require toHaveBeenCalledWith() instead of toHaveBeenCalled()

example.ts1
import { expect, mock, test } from "bun:test";

test("sends a welcome email", () => {
  const send = mock();
  register("ada@example.com", send);
  expect(send).toHaveBeenCalled();
});

bun:test/prefer-comparison-matcherhas suggestions

Require matchers such as toBeGreaterThan() instead of comparing inside expect()

example.ts2
import { expect, test } from "bun:test";

test("applies the discount", () => {
  const price = discounted(100);
  expect(price < 100).toBe(true);
  expect(price < 0).toBe(false);
});

bun:test/prefer-describe-function-titlehas suggestions

Require describe(fn, ...) instead of a string with the name of an imported function

example.ts1
import { describe, expect, test } from "bun:test";
import { slugify } from "./slugify";

describe("slugify", () => {
  test("replaces spaces", () => {
    expect(slugify("a b")).toBe("a-b");
  });
});

bun:test/prefer-each

Require test.each and describe.each instead of a loop around tests

example.ts1
import { expect, test } from "bun:test";

const cases = [
  [1, 2],
  [2, 4],
];

for (const [input, expected] of cases) {
  test(`doubles ${input}`, () => {
    expect(input * 2).toBe(expected);
  });
}

bun:test/prefer-ending-with-an-expect

Require the last statement of a test to be an assertion

example.ts1
import { expect, test } from "bun:test";

test("renames the user", async () => {
  const user = await createUser("Ada");
  expect(user.name).toBe("Ada");
  await user.rename("Grace");
});

bun:test/prefer-equality-matcherhas suggestions

Require toBe or toEqual instead of comparing with === inside expect

example.ts2
import { expect, test } from "bun:test";

test("the session belongs to the user", () => {
  expect(session.userId === user.id).toBe(true);
  expect(user.role !== "admin").toBe(true);
});

bun:test/prefer-expect-assertionshas suggestions

Require expect.assertions() or expect.hasAssertions() at the start of every test

example.ts1
import { expect, test } from "bun:test";

test("rejects a wrong password", async () => {
  try {
    await login("ada", "wrong");
  } catch (error) {
    expect(error).toBeInstanceOf(AuthError);
  }
});

bun:test/prefer-expect-resolveshas suggestions

Require await expect(promise).resolves instead of expect(await promise)

example.ts1
import { expect, test } from "bun:test";

test("loads the user", async () => {
  expect(await loadUser(1)).toEqual({ id: 1, name: "Ada" });
});

bun:test/prefer-expect-type-offixable

Require toBeTypeOf instead of expect(typeof value).toBe(...)

example.ts2
import { expect, test } from "bun:test";

test("the user has a name", () => {
  expect(typeof user.name).toBe("string");
  expect(typeof user.save).not.toEqual("undefined");
});

bun:test/prefer-hooks-in-order

Require hooks to be written in the order they run

example.ts1
import { afterEach, beforeEach } from "bun:test";

afterEach(() => {
  db.rollback();
});

beforeEach(() => {
  db.begin();
});

bun:test/prefer-hooks-on-top

Require hooks to come before the tests in their block

example.ts1
import { beforeEach, expect, test } from "bun:test";

test("starts empty", () => {
  expect(cart.items).toHaveLength(0);
});

beforeEach(() => {
  cart.clear();
});

bun:test/prefer-lowercase-titlefixable

Require the titles of tests and describe blocks to start with a lowercase letter

example.ts1
import { expect, test } from "bun:test";

test("Adds an item", () => {
  expect(cart.add(item)).toBe(1);
});

bun:test/prefer-mock-promise-shorthandfixable

Require mockResolvedValue() and mockRejectedValue() for mocks that return a promise

example.ts2
import { mock } from "bun:test";

const fetchUser = mock().mockReturnValue(Promise.resolve({ name: "Ada" }));
const saveUser = mock().mockImplementationOnce(() => Promise.reject(new Error("offline")));

bun:test/prefer-mock-return-shorthandfixable

Require mockReturnValue() for a mock that only returns a value

example.ts2
import { mock } from "bun:test";

const getPort = mock().mockImplementation(() => 3000);

const getUser = mock().mockImplementationOnce(() => {
  return { name: "Ada" };
});

bun:test/prefer-snapshot-hint

Require a hint on toMatchSnapshot() and toThrowErrorMatchingSnapshot()

example.ts2
import { expect, test } from "bun:test";

test("renders the menu", () => {
  expect(renderMenu({ open: false })).toMatchSnapshot();
  expect(renderMenu({ open: true })).toMatchSnapshot();
});

bun:test/prefer-spy-onhas suggestions

Require spyOn() instead of assigning jest.fn() to a property

example.ts2
import { beforeEach, jest, mock } from "bun:test";

beforeEach(() => {
  Date.now = mock(() => 1700000000000);
  console.warn = jest.fn();
});

bun:test/prefer-strict-boolean-matchershas suggestions

Require toBe(true) and toBe(false) instead of toBeTruthy() and toBeFalsy()

example.ts2
import { expect, test } from "bun:test";

test("checks the password", () => {
  expect(isValid("correct horse")).toBeTruthy();
  expect(isValid("")).toBeFalsy();
});

bun:test/prefer-strict-equalhas suggestions

Require toStrictEqual() instead of toEqual()

example.ts1
import { expect, test } from "bun:test";

test("creates a user", () => {
  expect(createUser("Ada")).toEqual({ name: "Ada" });
});

bun:test/prefer-to-befixable

Require toBe() for primitive literals, and toBeNull(), toBeUndefined(), toBeNaN()

example.ts7
import { expect, test } from "bun:test";

test("parses the header", () => {
  expect(header.version).toEqual(2);
  expect(header.name).toStrictEqual("config");
  expect(header.parent).toBe(null);
  expect(header.comment).not.toBeDefined();
  expect(header.size).not.toBe(undefined);
  expect(header.date).not.toBeUndefined();
  expect(header.ratio).toEqual(NaN);
});

bun:test/prefer-to-be-falsyhas suggestions

Require toBeFalsy() instead of toBe(false)

example.ts2
import { expect, test } from "bun:test";

test("a new cart has no discount", () => {
  const cart = createCart();
  expect(cart.hasDiscount).toBe(false);
  expect(cart.isOpen).not.toEqual(false);
});

bun:test/prefer-to-be-truthyhas suggestions

Require toBeTruthy() instead of toBe(true)

example.ts2
import { expect, test } from "bun:test";

test("an admin can delete posts", () => {
  const user = createUser({ role: "admin" });
  expect(user.canDelete).toBe(true);
  expect(user.isBanned).not.toEqual(true);
});

bun:test/prefer-to-containhas suggestions

Require toContain() instead of comparing the result of includes() with a boolean

example.ts2
import { expect, test } from "bun:test";

test("a draft is tagged", () => {
  const tags = getTags(draft);
  expect(tags.includes("draft")).toBe(true);
  expect(tags.includes("published")).toEqual(false);
});

bun:test/prefer-to-have-been-calledfixable

Require not.toHaveBeenCalled() instead of toHaveBeenCalledTimes(0)

example.ts2
import { expect, mock, test } from "bun:test";

test("only notifies on failure", () => {
  const onError = mock();
  const onDone = mock();
  run(task, { onError, onDone });
  expect(onError).toHaveBeenCalledTimes(0);
  expect(onDone).not.toHaveBeenCalledTimes(0);
});

bun:test/prefer-to-have-been-called-timesfixable

Require toHaveBeenCalledTimes() instead of checking the length of mock.calls

example.ts1
import { expect, mock, test } from "bun:test";

test("saves each item once", () => {
  const save = mock();
  saveAll(["a", "b"], save);
  expect(save.mock.calls).toHaveLength(2);
});

bun:test/prefer-to-have-lengthfixable

Require toHaveLength() instead of comparing the length property

example.ts2
import { expect, test } from "bun:test";

test("finds the active users", () => {
  const users = findUsers({ active: true });
  expect(users.length).toBe(2);
  expect(users[0].roles.length).not.toEqual(0);
});

bun:test/prefer-todofixable

Require test.todo for tests that have no body yet

example.ts3
import { test } from "bun:test";

test("rejects an expired token", () => {});

test("refreshes the token");

test.skip("logs out", function () {});

bun:test/require-hook

Require setup and teardown code to be inside a hook

example.ts3
import { describe, expect, test } from "bun:test";

let db = connect();
db.seed(["ada", "grace"]);

describe("users", () => {
  clearCache();

  test("finds a user", () => {
    expect(db.find("ada")).toBeDefined();
  });
});

bun:test/require-mock-type-parameters

Require a type parameter on jest.fn() and vi.fn()

example.ts2
import { jest, vi } from "bun:test";

const findUser = jest.fn();
const log = vi.fn((message: string) => {});

bun:test/require-test-timeout

Require every test to set a timeout

example.ts1
import { expect, test } from "bun:test";

test("loads the report", async () => {
  expect(await loadReport()).toBeDefined();
});

bun:test/require-to-throw-message

Require toThrow() to say which error it expects

example.ts3
import { expect, test } from "bun:test";

test("rejects a negative amount", async () => {
  expect(() => withdraw(-5)).toThrow();
  expect(() => withdraw(-5)).toThrowError();
  await expect(transfer(-5)).rejects.toThrow();
});

bun:test/require-top-level-describe

Require tests and hooks to be inside a describe block

example.ts2
import { beforeEach, expect, test } from "bun:test";

beforeEach(() => {
  cart.clear();
});

test("starts empty", () => {
  expect(cart.size).toBe(0);
});

bun:test/valid-describe-callbackon by default

Require describe to get a callback that is a function with no parameters

example.ts3
import { describe, test } from "bun:test";

describe("cart"); // no callback

describe("cart", "adds an item"); // not a function

describe("cart", (done) => {
  test("adds an item", () => {});
  done();
});

bun:test/valid-expecton by defaulthas suggestions

Require expect to get a value, end in a called matcher, and be awaited when it is async

example.ts4
import { expect, test } from "bun:test";

test("adds the tax", () => {
  expect(total(100)); // no matcher
  expect(total(100)).toBe; // the matcher is not called
  expect().toBe(108); // no value
  expect(total(100)).never.toBe(0); // `never` is not a modifier
});

bun:test/valid-expect-in-promiseon by default

Require a promise chain that contains expect to be awaited or returned from the test

example.ts1
import { expect, test } from "bun:test";

test("loads the user", () => {
  loadUser(1).then((user) => {
    expect(user.name).toBe("Ada");
  });
});

bun:test/valid-titlefixable

Require test and describe titles to be non-empty, with no stray spaces or repeated prefix

example.ts5
import { describe, test } from "bun:test";

describe("describe cart", () => {
  test("", () => {});
  test(" adds an item", () => {});
  test("test removes an item", () => {});
  test(404, () => {});
});

bun:test/warn-todo

Disallow .todo on test, it and describe

example.ts2
import { describe, test } from "bun:test";

test.todo("refunds the order");

describe.todo("checkout");

Jest 60 rules

jest/consistent-test-itfixable

Enforce one of test or it for defining tests

example.ts2
import { describe, expect, it, test } from "@jest/globals";

it("adds", () => {
  expect(1 + 1).toBe(2);
});

describe("subtract", () => {
  test("goes below zero", () => {
    expect(1 - 2).toBe(-1);
  });
});

jest/expect-expect

Require every test to make at least one assertion

example.ts2
import { test } from "@jest/globals";

test("parses the port", () => {
  parseConfig("port = 3000");
});

test.skip("parses the host", () => {
  parseConfig("host = localhost");
});

jest/max-expects

Enforce a maximum number of expect() calls in a test

example.ts1
import { expect, test } from "@jest/globals";

test("creates a user", () => {
  const user = createUser({ name: "Ada", email: "ada@example.com" });
  expect(user.id).toBeGreaterThan(0);
  expect(user.name).toBe("Ada");
  expect(user.email).toBe("ada@example.com");
  expect(user.role).toBe("member");
  expect(user.active).toBe(true);
  expect(user.createdAt).toBeInstanceOf(Date);
});

jest/max-nested-describe

Enforce a maximum depth of nested describe() calls

example.ts1
import { describe, expect, test } from "@jest/globals";

describe("cart", () => {
  describe("checkout", () => {
    describe("with a coupon", () => {
      test("lowers the total", () => {
        expect(checkout({ coupon: "SAVE10" }).total).toBe(90);
      });
    });
  });
});

jest/no-alias-methodsfixable

Disallow matcher aliases such as toBeCalled in favor of toHaveBeenCalled

example.ts1
import { expect, jest, test } from "@jest/globals";

test("saves the draft", () => {
  const save = jest.fn();
  save("draft");
  expect(save).toBeCalledWith("draft");
});

jest/no-commented-out-tests

Disallow tests that are inside a comment

example.ts1
import { expect, test } from "@jest/globals";

// test("rounds down", () => {
//   expect(Math.floor(1.9)).toBe(1);
// });

jest/no-conditional-expect

Disallow expect inside if, catch and other code that only runs sometimes

example.ts1
import { expect, test } from "@jest/globals";

test("rejects bad JSON", () => {
  try {
    JSON.parse("{");
  } catch (error) {
    expect(error).toBeInstanceOf(SyntaxError);
  }
});

jest/no-conditional-in-test

Disallow if, switch, ? :, &&, || and ?? inside a test

example.ts1
import { expect, test } from "@jest/globals";
import { label, users } from "./users";

test("labels every user", () => {
  for (const user of users) {
    const expected = user.admin ? "Admin" : "Member";
    expect(label(user)).toBe(expected);
  }
});

jest/no-confusing-set-timeout

Disallow jest.setTimeout inside a block, after a test, or more than once in a file

example.ts2
import { describe, expect, jest, test } from "@jest/globals";
import { upload } from "./upload";

describe("upload", () => {
  jest.setTimeout(10_000);

  test("sends the file", async () => {
    expect(await upload("report.pdf")).toBe(true);
  });
});

jest/no-deprecated-functionsfixable

Disallow Jest functions that have been renamed or replaced

example.ts2
import { jest } from "@jest/globals";

jest.useFakeTimers();
jest.runTimersToTime(1000);

const api = jest.genMockFromModule("./api");

jest/no-disabled-tests

Disallow skipped tests such as test.skip, xtest and xdescribe

example.ts2
import { describe, expect, test, xtest } from "@jest/globals";

describe.skip("checkout", () => {
  test("adds the tax", () => {
    expect(total(100)).toBe(108);
  });
});

xtest("applies the coupon", () => {
  expect(total(100, "SAVE10")).toBe(97.2);
});

jest/no-done-callback

Disallow the done callback in tests and hooks in favor of promises

example.ts1
import { expect, test } from "@jest/globals";

test("reads the config", done => {
  readConfig((error, config) => {
    expect(config.port).toBe(3000);
    done();
  });
});

jest/no-duplicate-hooks

Disallow using the same hook twice in one describe block

example.ts1
import { beforeEach, describe, test } from "@jest/globals";

describe("cart", () => {
  beforeEach(() => {
    cart.clear();
  });

  test("starts empty", () => {});

  beforeEach(() => {
    cart.add("apple");
  });
});

jest/no-export

Disallow exports from a file that has tests

src/cart.test.ts1
import { expect, test } from "@jest/globals";

export function makeCart() {
  return { items: [] };
}

test("starts empty", () => {
  expect(makeCart().items).toEqual([]);
});

jest/no-focused-testshas suggestions

Disallow test.only, describe.only, fit and fdescribe

example.ts2
import { expect, fit, test } from "@jest/globals";

test.only("adds", () => {
  expect(1 + 1).toBe(2);
});

fit("subtracts", () => {
  expect(2 - 1).toBe(1);
});

jest/no-hooks

Disallow beforeAll, beforeEach, afterEach and afterAll

example.ts1
import { beforeEach, expect, test } from "@jest/globals";

let cart;

beforeEach(() => {
  cart = createCart();
});

test("starts empty", () => {
  expect(cart.items).toEqual([]);
});

jest/no-identical-title

Disallow two tests or two describe blocks with the same title in one block

example.ts1
import { describe, expect, test } from "@jest/globals";

describe("parsePrice", () => {
  test("reads a number", () => {
    expect(parsePrice("12")).toBe(12);
  });

  test("reads a number", () => {
    expect(parsePrice("12.50")).toBe(12.5);
  });
});

jest/no-interpolation-in-snapshots

Disallow ${} in inline snapshots

example.ts1
import { expect, test } from "@jest/globals";

test("creates a user", () => {
  const user = createUser("Ada");
  expect(user).toMatchInlineSnapshot(`
    {
      "id": ${user.id},
      "name": "Ada",
    }
  `);
});

jest/no-jasmine-globalsfixable

Disallow Jasmine globals such as jasmine.any, spyOn, fail and pending

example.js4
jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;

test("saves the user", () => {
  spyOn(db, "insert");
  save({ name: "Ada" });
  expect(db.insert).toHaveBeenCalledWith(jasmine.any(Object));
});

test("exports to PDF", () => {
  pending();
});

jest/no-large-snapshots

Disallow snapshots that are longer than a set number of lines

example.ts1
import { expect, test } from "@jest/globals";

test("loads the user", () => {
  expect(loadUser(1)).toMatchInlineSnapshot(`
    {
      "email": "ada@example.com",
      "id": 1,
      "name": "Ada",
    }
  `);
});

jest/no-mocks-import

Disallow importing from a __mocks__ directory

example.ts1
import { expect, test } from "@jest/globals";
import { sendEmail } from "./__mocks__/mailer";
import { register } from "./register";

test("sends a welcome email", () => {
  register("ada@example.com");
  expect(sendEmail).toHaveBeenCalled();
});

jest/no-restricted-jest-methods

Disallow the jest methods that you list

example.ts2
import { expect, jest, test } from "@jest/globals";

test("retries after a second", () => {
  const send = jest.spyOn(mailer, "send");
  retry(send);
  jest.advanceTimersByTime(1000);
  expect(send).toHaveBeenCalledTimes(2);
});

jest/no-restricted-matchers

Disallow the matchers and modifiers you list

example.ts1
import { expect, test } from "@jest/globals";

test("a new user is active", () => {
  const user = { active: true };
  expect(user.active).toBeTruthy();
});

jest/no-standalone-expect

Disallow expect outside of a test or it block

example.ts2
import { beforeEach, describe, expect } from "@jest/globals";

describe("cart", () => {
  const cart = createCart();
  expect(cart.items).toHaveLength(0);

  beforeEach(() => {
    expect(cart.total).toBe(0);
  });
});

jest/no-test-prefixesfixable

Require .only and .skip instead of the f and x prefixes

example.ts3
import { expect, fit, xdescribe, xtest } from "@jest/globals";

fit("adds", () => {
  expect(1 + 1).toBe(2);
});

xtest("subtracts", () => {
  expect(3 - 1).toBe(2);
});

xdescribe("division", () => {});

jest/no-test-return-statement

Disallow return expect(..) in a test

example.ts1
import { expect, test } from "@jest/globals";

test("adds the prices", () => {
  const total = sum([1, 2]);
  return expect(total).toBe(3);
});

jest/no-unneeded-async-expect-functionfixable

Disallow an async function around a single awaited call in expect

example.ts2
import { expect, test } from "@jest/globals";

test("rejects an empty name", async () => {
  await expect(async () => {
    await saveUser({ name: "" });
  }).rejects.toThrow("name is required");

  await expect(async () => await saveUser({ name: "" })).rejects.toThrow();
});

jest/no-untyped-mock-factoryfixable

Require a type parameter on jest.mock() and jest.doMock() with a factory

example.ts1
import { jest } from "@jest/globals";

jest.mock("./mailer", () => ({
  send: jest.fn(),
}));

jest/padding-around-after-all-blocksfixable

Require a blank line before each afterAll call

example.js1
import { afterAll, describe } from "@jest/globals";

describe("database", () => {
  const db = connect();
  afterAll(() => db.close());
});

jest/padding-around-test-blocksfixable

Require a blank line before each test and it call

example.js2
import { describe, expect, test } from "@jest/globals";

describe("cart", () => {
  const cart = createCart();
  test("starts empty", () => {
    expect(cart.items).toEqual([]);
  });
  test.skip("adds an item", () => {
    expect(cart.add("apple").items).toEqual(["apple"]);
  });
});

jest/prefer-called-withfixable

Require toHaveBeenCalledWith() instead of toHaveBeenCalled()

example.js1
import { expect, jest, test } from "@jest/globals";

test("sends a welcome email", () => {
  const send = jest.fn();
  register("ada@example.com", send);
  expect(send).toHaveBeenCalled();
});

jest/prefer-comparison-matcherfixable

Require matchers such as toBeGreaterThan() instead of comparing inside expect()

example.js2
import { expect, test } from "@jest/globals";

test("applies the discount", () => {
  const price = discounted(100);
  expect(price < 100).toBe(true);
  expect(price < 0).toBe(false);
});

jest/prefer-each

Require test.each and describe.each instead of a loop around tests

example.ts1
import { expect, test } from "@jest/globals";

const cases = [
  [1, 2],
  [2, 4],
];

for (const [input, expected] of cases) {
  test(`doubles ${input}`, () => {
    expect(input * 2).toBe(expected);
  });
}

jest/prefer-ending-with-an-expect

Require the last statement of a test to be an assertion

example.ts1
import { expect, test } from "@jest/globals";

test("renames the user", async () => {
  const user = await createUser("Ada");
  expect(user.name).toBe("Ada");
  await user.rename("Grace");
});

jest/prefer-equality-matcherhas suggestions

Require toBe or toEqual instead of comparing with === inside expect

example.ts2
import { expect, test } from "@jest/globals";

test("the session belongs to the user", () => {
  expect(session.userId === user.id).toBe(true);
  expect(user.role !== "admin").toBe(true);
});

jest/prefer-expect-assertionshas suggestions

Require expect.assertions() or expect.hasAssertions() at the start of every test

example.ts1
import { expect, test } from "@jest/globals";

test("rejects a wrong password", async () => {
  try {
    await login("ada", "wrong");
  } catch (error) {
    expect(error).toBeInstanceOf(AuthError);
  }
});

jest/prefer-expect-resolvesfixable

Require await expect(promise).resolves instead of expect(await promise)

example.ts1
import { expect, test } from "@jest/globals";

test("loads the user", async () => {
  expect(await loadUser(1)).toEqual({ id: 1, name: "Ada" });
});

jest/prefer-hooks-in-order

Require hooks to be written in the order they run

example.ts1
import { afterEach, beforeEach } from "@jest/globals";

afterEach(() => {
  db.rollback();
});

beforeEach(() => {
  db.begin();
});

jest/prefer-hooks-on-top

Require hooks to come before the tests in their block

example.ts1
import { beforeEach, expect, test } from "@jest/globals";

test("starts empty", () => {
  expect(cart.items).toHaveLength(0);
});

beforeEach(() => {
  cart.clear();
});

jest/prefer-importing-jest-globalsfixable

Require describe, test, expect and jest to be imported from @jest/globals

example.ts1
describe("cart", () => {
  test("starts empty", () => {
    expect(cart.items).toHaveLength(0);
  });
});

jest/prefer-jest-mockedfixable

Require jest.mocked(fn) instead of fn as jest.Mock

example.ts2
import { jest } from "@jest/globals";
import { fetchUser, saveUser } from "./api";

jest.mock("./api");

(fetchUser as jest.Mock).mockReturnValue({ name: "Ada" });
const mockSaveUser = saveUser as jest.MockedFunction<typeof saveUser>;

jest/prefer-lowercase-titlefixable

Require the titles of tests and describe blocks to start with a lowercase letter

example.ts1
import { expect, test } from "@jest/globals";

test("Adds an item", () => {
  expect(cart.add(item)).toBe(1);
});

jest/prefer-mock-promise-shorthandfixable

Require mockResolvedValue() and mockRejectedValue() for mocks that return a promise

example.ts2
import { jest } from "@jest/globals";

const fetchUser = jest.fn().mockReturnValue(Promise.resolve({ name: "Ada" }));
const saveUser = jest.fn().mockImplementationOnce(() => Promise.reject(new Error("offline")));

jest/prefer-mock-return-shorthandfixable

Require mockReturnValue() for a mock that only returns a value

example.ts2
import { jest } from "@jest/globals";

const getPort = jest.fn().mockImplementation(() => 3000);

const getUser = jest.fn().mockImplementationOnce(() => {
  return { name: "Ada" };
});

jest/prefer-snapshot-hint

Require a hint on toMatchSnapshot() and toThrowErrorMatchingSnapshot()

example.ts2
import { expect, test } from "@jest/globals";

test("renders the menu", () => {
  expect(renderMenu({ open: false })).toMatchSnapshot();
  expect(renderMenu({ open: true })).toMatchSnapshot();
});

jest/prefer-spy-onhas suggestions

Require jest.spyOn() instead of assigning jest.fn() to a property

example.ts2
import { beforeEach, jest } from "@jest/globals";

beforeEach(() => {
  Date.now = jest.fn(() => 1700000000000);
  console.warn = jest.fn();
});

jest/prefer-strict-equalhas suggestions

Require toStrictEqual() instead of toEqual()

example.ts1
import { expect, test } from "@jest/globals";

test("creates a user", () => {
  expect(createUser("Ada")).toEqual({ name: "Ada" });
});

jest/prefer-to-befixable

Require toBe() for primitive literals, and toBeNull(), toBeUndefined(), toBeNaN()

example.ts7
import { expect, test } from "@jest/globals";

test("parses the header", () => {
  expect(header.version).toEqual(2);
  expect(header.name).toStrictEqual("config");
  expect(header.parent).toBe(null);
  expect(header.comment).not.toBeDefined();
  expect(header.size).not.toBe(undefined);
  expect(header.date).not.toBeUndefined();
  expect(header.ratio).toEqual(NaN);
});

jest/prefer-to-containfixable

Require toContain() instead of comparing the result of includes() with a boolean

example.ts2
import { expect, test } from "@jest/globals";

test("a draft is tagged", () => {
  const tags = getTags(draft);
  expect(tags.includes("draft")).toBe(true);
  expect(tags.includes("published")).toEqual(false);
});

jest/prefer-to-have-been-calledfixable

Require not.toHaveBeenCalled() instead of toHaveBeenCalledTimes(0)

example.ts2
import { expect, jest, test } from "@jest/globals";

test("only notifies on failure", () => {
  const onError = jest.fn();
  const onDone = jest.fn();
  run(task, { onError, onDone });
  expect(onError).toHaveBeenCalledTimes(0);
  expect(onDone).not.toHaveBeenCalledTimes(0);
});

jest/prefer-to-have-been-called-timesfixable

Require toHaveBeenCalledTimes() instead of checking the length of mock.calls

example.ts1
import { expect, jest, test } from "@jest/globals";

test("saves each item once", () => {
  const save = jest.fn();
  saveAll(["a", "b"], save);
  expect(save.mock.calls).toHaveLength(2);
});

jest/prefer-to-have-lengthfixable

Require toHaveLength() instead of comparing the length property

example.ts2
import { expect, test } from "@jest/globals";

test("finds the active users", () => {
  const users = findUsers({ active: true });
  expect(users.length).toBe(2);
  expect(users[0].roles.length).not.toEqual(0);
});

jest/prefer-todofixable

Require test.todo for tests that have no body yet

example.js3
import { test } from "@jest/globals";

test("rejects an expired token", () => {});

test("refreshes the token");

test.skip("logs out", function () {});

jest/require-hook

Require setup and teardown code to be inside a hook

example.js3
import { describe, expect, test } from "@jest/globals";

let db = connect();
db.seed(["ada", "grace"]);

describe("users", () => {
  clearCache();

  test("finds a user", () => {
    expect(db.find("ada")).toBeDefined();
  });
});

jest/require-to-throw-message

Require toThrow() to say which error it expects

example.js3
import { expect, test } from "@jest/globals";

test("rejects a negative amount", async () => {
  expect(() => withdraw(-5)).toThrow();
  expect(() => withdraw(-5)).toThrowError();
  await expect(transfer(-5)).rejects.toThrow();
});

jest/require-top-level-describe

Require tests and hooks to be inside a describe block

example.js2
import { beforeEach, expect, test } from "@jest/globals";

beforeEach(() => {
  cart.clear();
});

test("starts empty", () => {
  expect(cart.size).toBe(0);
});

jest/valid-describe-callback

Require describe to get a title and a plain synchronous callback

example.ts1
import { describe, expect, test } from "@jest/globals";

describe("cart", async () => {
  const items = await loadItems();

  test("has items", () => {
    expect(items.length).toBeGreaterThan(0);
  });
});

jest/valid-expecthas suggestions

Require expect to get a value, end in a called matcher, and be awaited when it is async

example.ts5
import { expect, test } from "@jest/globals";

test("adds the tax", () => {
  expect(total(100)); // no matcher
  expect(total(100)).toBe; // the matcher is not called
  expect().toBe(108); // no value
  expect(total(100), "after tax").toBe(108); // more than one argument
  expect(total(100)).never.toBe(0); // `never` is not a modifier
});

jest/valid-expect-in-promise

Require a promise chain that contains expect to be awaited or returned from the test

example.ts1
import { expect, test } from "@jest/globals";

test("loads the user", () => {
  loadUser(1).then((user) => {
    expect(user.name).toBe("Ada");
  });
});

jest/valid-titlefixable

Require test and describe titles to be non-empty strings with no stray spaces or repeated prefix

example.ts5
import { describe, test } from "@jest/globals";

describe("describe cart", () => {
  test("", () => {});
  test(" adds an item", () => {});
  test("test removes an item", () => {});
  test(404, () => {});
});

Vitest 73 rules

vitest/consistent-each-for

Enforce either .each or .for for parameterized tests

example.ts1
import { expect, test } from "vitest";

test.each([
  [1, 2, 3],
  [2, 3, 5],
])("adds %i and %i", (a, b, sum) => {
  expect(a + b).toBe(sum);
});

vitest/consistent-test-filename

Require test files to follow one naming pattern

src/user.spec.ts1
import { expect, test } from "vitest";

test("has a name", () => {
  expect(loadUser().name).toBe("Ada");
});

vitest/consistent-test-itfixable

Enforce one of test or it for defining tests

example.ts2
import { describe, expect, it, test } from "vitest";

it("adds", () => {
  expect(1 + 1).toBe(2);
});

describe("subtract", () => {
  test("goes below zero", () => {
    expect(1 - 2).toBe(-1);
  });
});

vitest/consistent-vitest-vifixable

Enforce one of vi or vitest for Vitest's helper object

example.ts2
import { expect, test, vitest } from "vitest";

test("calls the listener", () => {
  const listener = vitest.fn();
  emit("save", listener);
  expect(listener).toHaveBeenCalled();
});

vitest/expect-expect

Require every test to make at least one assertion

example.ts1
import { test } from "vitest";

test("parses the port", () => {
  parseConfig("port = 3000");
});

vitest/hoisted-apis-on-tophas suggestions

Require vi.mock, vi.unmock and vi.hoisted at the top level of the file

example.ts1
import { beforeEach, vi } from "vitest";

beforeEach(() => {
  vi.mock("./database");
});

vitest/max-expects

Enforce a maximum number of expect() calls in a test

example.ts1
import { expect, test } from "vitest";

test("creates a user", () => {
  const user = createUser({ name: "Ada", email: "ada@example.com" });
  expect(user.id).toBeGreaterThan(0);
  expect(user.name).toBe("Ada");
  expect(user.email).toBe("ada@example.com");
  expect(user.role).toBe("member");
  expect(user.active).toBe(true);
  expect(user.createdAt).toBeInstanceOf(Date);
});

vitest/max-nested-describe

Enforce a maximum depth of nested describe() calls

example.ts1
import { describe, expect, test } from "vitest";

describe("cart", () => {
  describe("checkout", () => {
    describe("with a coupon", () => {
      test("lowers the total", () => {
        expect(checkout({ coupon: "SAVE10" }).total).toBe(90);
      });
    });
  });
});

vitest/no-alias-methodsfixable

Disallow matcher aliases such as toBeCalled in favor of toHaveBeenCalled

example.ts1
import { expect, test, vi } from "vitest";

test("saves the draft", () => {
  const save = vi.fn();
  save("draft");
  expect(save).toBeCalledWith("draft");
});

vitest/no-commented-out-tests

Disallow tests that are inside a comment

example.ts1
import { expect, test } from "vitest";

// test("rounds down", () => {
//   expect(Math.floor(1.9)).toBe(1);
// });

vitest/no-conditional-expect

Disallow expect inside if, catch and other code that only runs sometimes

example.ts1
import { expect, test } from "vitest";

test("rejects bad JSON", () => {
  try {
    JSON.parse("{");
  } catch (error) {
    expect(error).toBeInstanceOf(SyntaxError);
  }
});

vitest/no-conditional-in-test

Disallow if, switch, ? :, &&, || and ?? inside a test

example.ts1
import { expect, test } from "vitest";
import { label, users } from "./users";

test("labels every user", () => {
  for (const user of users) {
    const expected = user.admin ? "Admin" : "Member";
    expect(label(user)).toBe(expected);
  }
});

vitest/no-conditional-tests

Disallow test and describe inside an if statement

example.ts1
import { expect, test } from "vitest";
import { readFile } from "node:fs/promises";

if (process.platform === "linux") {
  test("reads the kernel version", async () => {
    expect(await readFile("/proc/version", "utf8")).toContain("Linux");
  });
}

vitest/no-disabled-tests

Disallow skipped tests such as test.skip and describe.skip

example.ts2
import { describe, expect, test } from "vitest";

describe.skip("checkout", () => {
  test("adds the tax", () => {
    expect(total(100)).toBe(108);
  });
});

test.skip("applies the coupon", () => {
  expect(total(100, "SAVE10")).toBe(97.2);
});

vitest/no-duplicate-hooks

Disallow using the same hook twice in one describe block

example.ts1
import { beforeEach, describe, test } from "vitest";

describe("cart", () => {
  beforeEach(() => {
    cart.clear();
  });

  test("starts empty", () => {});

  beforeEach(() => {
    cart.add("apple");
  });
});

vitest/no-focused-testshas suggestions

Disallow test.only and describe.only

example.ts2
import { describe, expect, test } from "vitest";

test.only("adds", () => {
  expect(1 + 1).toBe(2);
});

describe.only("subtraction", () => {
  test("subtracts", () => {
    expect(2 - 1).toBe(1);
  });
});

vitest/no-hooks

Disallow beforeAll, beforeEach, afterEach and afterAll

example.ts1
import { beforeEach, expect, test } from "vitest";

let cart;

beforeEach(() => {
  cart = createCart();
});

test("starts empty", () => {
  expect(cart.items).toEqual([]);
});

vitest/no-identical-title

Disallow two tests or two describe blocks with the same title in one block

example.ts1
import { describe, expect, test } from "vitest";

describe("parsePrice", () => {
  test("reads a number", () => {
    expect(parsePrice("12")).toBe(12);
  });

  test("reads a number", () => {
    expect(parsePrice("12.50")).toBe(12.5);
  });
});

vitest/no-import-node-testhas suggestions

Disallow importing from node:test in a project that uses Vitest

example.ts1
import { describe, it } from "node:test";

describe("slugify", () => {
  it("lowercases the title", () => {});
});

vitest/no-importing-vitest-globalsfixable

Disallow importing test, expect, vi and the other Vitest globals

example.ts1
import { describe, expect, it } from "vitest";

describe("slugify", () => {
  it("lowercases the title", () => {
    expect(slugify("Hello")).toBe("hello");
  });
});

vitest/no-interpolation-in-snapshots

Disallow ${} in inline snapshots

example.ts1
import { expect, test } from "vitest";

test("creates a user", () => {
  const user = createUser("Ada");
  expect(user).toMatchInlineSnapshot(`
    {
      "id": ${user.id},
      "name": "Ada",
    }
  `);
});

vitest/no-large-snapshots

Disallow snapshots that are longer than a set number of lines

example.ts1
import { expect, test } from "vitest";

test("loads the user", () => {
  expect(loadUser(1)).toMatchInlineSnapshot(`
    {
      "email": "ada@example.com",
      "id": 1,
      "name": "Ada",
    }
  `);
});

vitest/no-mocks-import

Disallow importing from a __mocks__ directory

example.ts1
import { expect, test } from "vitest";
import { sendEmail } from "./__mocks__/mailer";
import { register } from "./register";

test("sends a welcome email", () => {
  register("ada@example.com");
  expect(sendEmail).toHaveBeenCalled();
});

vitest/no-restricted-matchers

Disallow the matchers and modifiers you list

example.ts1
import { expect, test } from "vitest";

test("a new user is active", () => {
  const user = { active: true };
  expect(user.active).toBeTruthy();
});

vitest/no-standalone-expect

Disallow expect outside of a test or it block

example.ts2
import { beforeEach, describe, expect } from "vitest";

describe("cart", () => {
  const cart = createCart();
  expect(cart.items).toHaveLength(0);

  beforeEach(() => {
    expect(cart.total).toBe(0);
  });
});

vitest/no-test-prefixesfixable

Require .only and .skip instead of the f and x prefixes

example.ts3
import { expect } from "vitest";

fit("adds", () => {
  expect(1 + 1).toBe(2);
});

xtest("subtracts", () => {
  expect(3 - 1).toBe(2);
});

xdescribe("division", () => {});

vitest/no-test-return-statement

Disallow return expect(..) in a test

example.ts1
import { expect, test } from "vitest";

test("adds the prices", () => {
  const total = sum([1, 2]);
  return expect(total).toBe(3);
});

vitest/no-unneeded-async-expect-functionfixable

Disallow an async function around a single awaited call in expect

example.ts2
import { expect, test } from "vitest";

test("rejects an empty name", async () => {
  await expect(async () => {
    await saveUser({ name: "" });
  }).rejects.toThrow("name is required");

  await expect(async () => await saveUser({ name: "" })).rejects.toThrow();
});

vitest/padding-around-after-all-blocksfixable

Require a blank line before each afterAll call

example.ts1
import { afterAll, describe } from "vitest";

describe("database", () => {
  const db = connect();
  afterAll(() => db.close());
});

vitest/padding-around-test-blocksfixable

Require a blank line before each test and it call

example.ts2
import { describe, expect, test } from "vitest";

describe("cart", () => {
  const cart = createCart();
  test("starts empty", () => {
    expect(cart.items).toEqual([]);
  });
  test.skip("adds an item", () => {
    expect(cart.add("apple").items).toEqual(["apple"]);
  });
});

vitest/prefer-called-exactly-once-withfixable

Require toHaveBeenCalledExactlyOnceWith() over separate call count and argument checks

example.ts1
import { expect, test, vi } from "vitest";

test("saves the draft", () => {
  const save = vi.fn();
  autosave("draft", save);
  expect(save).toHaveBeenCalledOnce();
  expect(save).toHaveBeenCalledWith("draft");
});

vitest/prefer-called-oncefixable

Require toHaveBeenCalledOnce() instead of toHaveBeenCalledTimes(1)

example.ts1
import { expect, test, vi } from "vitest";

test("notifies the listener", () => {
  const listener = vi.fn();
  publish("ready", listener);
  expect(listener).toHaveBeenCalledTimes(1);
});

vitest/prefer-called-timesfixable

Require toHaveBeenCalledTimes(1) instead of toHaveBeenCalledOnce()

example.ts1
import { expect, test, vi } from "vitest";

test("does not retry after a success", () => {
  const request = vi.fn();
  retryFetch(request);
  expect(request).toHaveBeenCalledOnce();
});

vitest/prefer-called-withfixable

Require toHaveBeenCalledWith() instead of toHaveBeenCalled()

example.ts1
import { expect, test, vi } from "vitest";

test("sends a welcome email", () => {
  const send = vi.fn();
  register("ada@example.com", send);
  expect(send).toHaveBeenCalled();
});

vitest/prefer-comparison-matcherfixable

Require matchers such as toBeGreaterThan() instead of comparing inside expect()

example.ts2
import { expect, test } from "vitest";

test("applies the discount", () => {
  const price = discounted(100);
  expect(price < 100).toBe(true);
  expect(price < 0).toBe(false);
});

vitest/prefer-describe-function-titlefixable

Require describe(fn, ...) instead of a string with the name of an imported function

example.ts1
import { describe, expect, test } from "vitest";
import { slugify } from "./slugify";

describe("slugify", () => {
  test("replaces spaces", () => {
    expect(slugify("a b")).toBe("a-b");
  });
});

vitest/prefer-each

Require test.each and describe.each instead of a loop around tests

example.ts1
import { expect, test } from "vitest";

const cases = [
  [1, 2],
  [2, 4],
];

for (const [input, expected] of cases) {
  test(`doubles ${input}`, () => {
    expect(input * 2).toBe(expected);
  });
}

vitest/prefer-equality-matcherhas suggestions

Require toBe or toEqual instead of comparing with === inside expect

example.ts2
import { expect, test } from "vitest";

test("the session belongs to the user", () => {
  expect(session.userId === user.id).toBe(true);
  expect(user.role !== "admin").toBe(true);
});

vitest/prefer-expect-assertionshas suggestions

Require expect.assertions() or expect.hasAssertions() at the start of every test

example.ts1
import { expect, test } from "vitest";

test("rejects a wrong password", async () => {
  try {
    await login("ada", "wrong");
  } catch (error) {
    expect(error).toBeInstanceOf(AuthError);
  }
});

vitest/prefer-expect-resolvesfixable

Require await expect(promise).resolves instead of expect(await promise)

example.ts1
import { expect, test } from "vitest";

test("loads the user", async () => {
  expect(await loadUser(1)).toEqual({ id: 1, name: "Ada" });
});

vitest/prefer-expect-type-offixable

Require toBeTypeOf instead of expect(typeof value).toBe(...)

example.ts2
import { expect, test } from "vitest";

test("the user has a name", () => {
  expect(typeof user.name).toBe("string");
  expect(typeof user.save).not.toEqual("undefined");
});

vitest/prefer-hooks-in-order

Require hooks to be written in the order they run

example.ts1
import { afterEach, beforeEach } from "vitest";

afterEach(() => {
  db.rollback();
});

beforeEach(() => {
  db.begin();
});

vitest/prefer-hooks-on-top

Require hooks to come before the tests in their block

example.ts1
import { beforeEach, expect, test } from "vitest";

test("starts empty", () => {
  expect(cart.items).toHaveLength(0);
});

beforeEach(() => {
  cart.clear();
});

vitest/prefer-import-in-mockfixable

Require import() instead of a string path in vi.mock() and vi.doMock()

example.ts1
import { vi } from "vitest";

vi.mock("./api", async importOriginal => {
  const api = await importOriginal();
  return { ...api, fetchUser: vi.fn() };
});

vitest/prefer-importing-vitest-globalsfixable

Require describe, test, expect and vi to be imported from vitest

example.ts1
describe("cart", () => {
  test("starts empty", () => {
    expect(cart.items).toHaveLength(0);
  });
});

vitest/prefer-lowercase-titlefixable

Require the titles of tests and describe blocks to start with a lowercase letter

example.ts1
import { expect, test } from "vitest";

test("Adds an item", () => {
  expect(cart.add(item)).toBe(1);
});

vitest/prefer-mock-promise-shorthandfixable

Require mockResolvedValue() and mockRejectedValue() for mocks that return a promise

example.ts2
import { vi } from "vitest";

const fetchUser = vi.fn().mockReturnValue(Promise.resolve({ name: "Ada" }));
const saveUser = vi.fn().mockImplementationOnce(() => Promise.reject(new Error("offline")));

vitest/prefer-mock-return-shorthandfixable

Require mockReturnValue() for a mock that only returns a value

example.ts2
import { vi } from "vitest";

const getPort = vi.fn().mockImplementation(() => 3000);

const getUser = vi.fn().mockImplementationOnce(() => {
  return { name: "Ada" };
});

vitest/prefer-snapshot-hint

Require a hint on toMatchSnapshot() and toThrowErrorMatchingSnapshot()

example.ts2
import { expect, test } from "vitest";

test("renders the menu", () => {
  expect(renderMenu({ open: false })).toMatchSnapshot();
  expect(renderMenu({ open: true })).toMatchSnapshot();
});

vitest/prefer-spy-onhas suggestions

Require vi.spyOn() instead of assigning vi.fn() to a property

example.ts2
import { beforeEach, vi } from "vitest";

beforeEach(() => {
  Date.now = vi.fn(() => 1700000000000);
  console.warn = vi.fn();
});

vitest/prefer-strict-boolean-matchersfixable

Require toBe(true) and toBe(false) instead of toBeTruthy() and toBeFalsy()

example.ts2
import { expect, test } from "vitest";

test("checks the password", () => {
  expect(isValid("correct horse")).toBeTruthy();
  expect(isValid("")).toBeFalsy();
});

vitest/prefer-strict-equalhas suggestions

Require toStrictEqual() instead of toEqual()

example.ts1
import { expect, test } from "vitest";

test("creates a user", () => {
  expect(createUser("Ada")).toEqual({ name: "Ada" });
});

vitest/prefer-to-befixable

Require toBe() for primitive literals, and toBeNull(), toBeUndefined(), toBeNaN()

example.ts7
import { expect, test } from "vitest";

test("parses the header", () => {
  expect(header.version).toEqual(2);
  expect(header.name).toStrictEqual("config");
  expect(header.parent).toBe(null);
  expect(header.comment).not.toBeDefined();
  expect(header.size).not.toBe(undefined);
  expect(header.date).not.toBeUndefined();
  expect(header.ratio).toEqual(NaN);
});

vitest/prefer-to-be-falsyhas suggestions

Require toBeFalsy() instead of toBe(false)

example.ts2
import { expect, test } from "vitest";

test("a new cart has no discount", () => {
  const cart = createCart();
  expect(cart.hasDiscount).toBe(false);
  expect(cart.isOpen).not.toEqual(false);
});

vitest/prefer-to-be-objectfixable

Require toBeObject() when expectTypeOf() checks for an object

example.ts3
import { expectTypeOf, test } from "vitest";

test("the config is an object", () => {
  const config = loadConfig();
  expectTypeOf(config).toBeInstanceOf(Object);
  expectTypeOf(config instanceof Object).toBeTruthy();
  expectTypeOf(config.name instanceof Object).toBeFalsy();
});

vitest/prefer-to-be-truthyhas suggestions

Require toBeTruthy() instead of toBe(true)

example.ts2
import { expect, test } from "vitest";

test("an admin can delete posts", () => {
  const user = createUser({ role: "admin" });
  expect(user.canDelete).toBe(true);
  expect(user.isBanned).not.toEqual(true);
});

vitest/prefer-to-containfixable

Require toContain() instead of comparing the result of includes() with a boolean

example.ts2
import { expect, test } from "vitest";

test("a draft is tagged", () => {
  const tags = getTags(draft);
  expect(tags.includes("draft")).toBe(true);
  expect(tags.includes("published")).toEqual(false);
});

vitest/prefer-to-have-been-called-timesfixable

Require toHaveBeenCalledTimes() instead of checking the length of mock.calls

example.ts1
import { expect, test, vi } from "vitest";

test("saves each item once", () => {
  const save = vi.fn();
  saveAll(["a", "b"], save);
  expect(save.mock.calls).toHaveLength(2);
});

vitest/prefer-to-have-lengthfixable

Require toHaveLength() instead of comparing the length property

example.ts2
import { expect, test } from "vitest";

test("finds the active users", () => {
  const users = findUsers({ active: true });
  expect(users.length).toBe(2);
  expect(users[0].roles.length).not.toEqual(0);
});

vitest/prefer-todofixable

Require test.todo for tests that have no body yet

example.js3
import { test } from "vitest";

test("rejects an expired token", () => {});

test("refreshes the token");

test.skip("logs out", function () {});

vitest/require-awaited-expect-poll

Require expect.poll and expect.element assertions to be awaited or returned

example.js4
import { expect, test } from "vitest";

test("shows the saved message", () => {
  expect.poll(() => status.text).toBe("Saved");
  expect.element(banner).toBeVisible();
});

vitest/require-hook

Require setup and teardown code to be inside a hook

example.js3
import { describe, expect, test } from "vitest";

let db = connect();
db.seed(["ada", "grace"]);

describe("users", () => {
  clearCache();

  test("finds a user", () => {
    expect(db.find("ada")).toBeDefined();
  });
});

vitest/require-local-test-context-for-concurrent-snapshots

Require the expect of the test context for snapshots in concurrent tests

example.js2
import { describe, expect, test } from "vitest";

test.concurrent("renders the header", () => {
  expect(render("header")).toMatchSnapshot();
});

describe.concurrent("footer", () => {
  test("renders the links", () => {
    expect(render("links")).toMatchSnapshot();
  });
});

vitest/require-mock-type-parameters

Require a type parameter on vi.fn()

example.ts2
import { vi } from "vitest";

const findUser = vi.fn();
const log = vi.fn((message: string) => {});

vitest/require-test-timeout

Require every test to set a timeout

example.js1
import { expect, test } from "vitest";

test("loads the report", async () => {
  expect(await loadReport()).toBeDefined();
});

vitest/require-to-throw-message

Require toThrow() to say which error it expects

example.js3
import { expect, test } from "vitest";

test("rejects a negative amount", async () => {
  expect(() => withdraw(-5)).toThrow();
  expect(() => withdraw(-5)).toThrowError();
  await expect(transfer(-5)).rejects.toThrow();
});

vitest/require-top-level-describe

Require tests and hooks to be inside a describe block

example.js2
import { beforeEach, expect, test } from "vitest";

beforeEach(() => {
  cart.clear();
});

test("starts empty", () => {
  expect(cart.size).toBe(0);
});

vitest/valid-describe-callback

Require describe to get a title and a callback with no parameters and no return

example.ts5
import { describe, test } from "vitest";

describe("cart"); // no callback

describe("cart", "adds an item"); // not a function

describe("cart", (done) => {
  test("adds an item", () => {});
  done();
});

describe("cart", () => {
  return test("adds an item", () => {});
});

describe("cart", () => test("adds an item", () => {})); // returns the result of `test()`

vitest/valid-expecthas suggestions

Require expect to get a value, end in a called matcher, and be awaited when it is async

example.ts4
import { expect, test } from "vitest";

test("adds the tax", () => {
  expect(total(100)); // no matcher
  expect(total(100)).toBe; // the matcher is not called
  expect().toBe(108); // no value
  expect(total(100)).never.toBe(0); // `never` is not a modifier
});

vitest/valid-expect-in-promise

Require a promise chain that contains expect to be awaited or returned from the test

example.ts1
import { expect, test } from "vitest";

test("loads the user", () => {
  loadUser(1).then((user) => {
    expect(user.name).toBe("Ada");
  });
});

vitest/valid-titlefixable

Require test and describe titles to be non-empty strings with no stray spaces or repeated prefix

example.ts5
import { describe, test } from "vitest";

describe("describe cart", () => {
  test("", () => {});
  test(" adds an item", () => {});
  test("test removes an item", () => {});
  test(404, () => {});
});

vitest/warn-todo

Disallow .todo on test, it and describe

example.ts2
import { describe, test } from "vitest";

test.todo("refunds the order");

describe.todo("checkout");

Prettier 1 rule

prettier/prettierfixable

Require code to be formatted the way Prettier prints it

example.js1
const user = {name:'Ada'}