Skip to Content

Unicorn

Modernization and style rules from eslint-plugin-unicorn.

They span array iteration, string and regex idioms, Node.js APIs, error handling, module syntax, DOM APIs, and code shape.

They rewrite legacy patterns into modern counterparts, forbid known anti-patterns, and pin a consistent style for things Core and TypeScript leave underspecified.

Most rules are pure AST checks. Binding-aware rules use the TypeScript checker when lexical identity is part of the contract.

Source: eslint-plugin-unicorn (MIT).

Rule index

Each rule name links to the detailed section below.

Examples come from the checked lint corpus  or package-level rule tests when project layout matters.

Rules whose native port is registered but intentionally diagnostic-free are marked as illustrative in their detailed section.

Disallow

Prefer

Require

Consistency

Other checks

Rules

unicorn/better-regex

Rewrite regex literals into shorter, consistent, and safer form (character-class shorthands, redundant ranges).

Each regex literal is optimized the way regexp-tree canonicalizes it: character classes collapse to their shorthands ([0-9]\d, [^A-Za-z0-9_]\W), ranges are deduplicated and sorted, flags are alphabetized, and redundant quantifiers merge (\s?\s?\s?\s{0,3}). The autofix replaces the whole literal, except when it is the object of a .source or .toString member access — those are reported without a fix so a serialized-source consumer is not rewritten under it. Literals carrying the u or v flag are skipped, matching upstream, because regexp-tree does not handle Unicode / Unicode-sets mode reliably. A new RegExp("pattern", "flags") string constructor rewrites its pattern argument through the same character-class shorthands; the rewritten pattern keeps the argument’s original quote character and is re-escaped for it, so a pattern carrying a newline, a control character, or the delimiter quote comes back as a valid string literal.

The one option, sortCharacterClasses (default true), turns off only the class range sort/merge step when set to false; the length-reducing shorthands still apply.

Example:

// reports: unicorn/better-regex (error) — /[0-9]/ can be optimized to /\d/ const digits = /[0-9]/;
export default { rules: { "unicorn/better-regex": ["error", { sortCharacterClasses: false }], }, };

unicorn/catch-error-name

Enforce a canonical parameter name (error) in catch clauses.

Example:

// reports: unicorn/catch-error-name (error) try { } catch (err) { void err; }

unicorn/consistent-assert

Enforce consistent assertion style when using node:assert.

Example:

import assert from "node:assert"; // reports: unicorn/consistent-assert (error) assert.equal(1, 1);

unicorn/consistent-date-clone

Prefer passing a Date directly to the Date constructor when cloning, not +date or date.getTime().

Example:

const original = new Date(); // reports: unicorn/consistent-date-clone (error) const clone = new Date(original.getTime());

unicorn/consistent-destructuring

Once a property is destructured from an object, require subsequent reads to use the destructured binding.

Example:

const user = { profile: { name: "Ada" } }; const { profile } = user; // reports: unicorn/consistent-destructuring (error) console.log(user.profile.name);

unicorn/consistent-empty-array-spread

Require both branches of a ternary spread inside an array literal to be array-typed.

Example:

const cond = true; const x = 1; // reports: unicorn/consistent-empty-array-spread (error) const a = [1, ...(cond ? [x] : 2)];

unicorn/consistent-existence-index-check

Compare an index against the -1 sentinel instead of against zero. On a const bound to indexOf, lastIndexOf, findIndex, or findLastIndex, the rule rewrites index < 0 to index === -1, and index >= 0 / index > -1 to index !== -1.

Example:

const arr = [1, 2, 3]; const index = arr.indexOf(2); // reports: unicorn/consistent-existence-index-check (error) const found = index >= 0;

Only a comparison on a const index binding is reported, matching the upstream rule. An inline arr.indexOf(2) >= 0 binds no index and is left alone, as is an index declared with let or var.

unicorn/consistent-function-scoping

Hoist function declarations to the highest scope that does not capture any outer variables.

Example:

function formatNames(names: string[]) { // reports: unicorn/consistent-function-scoping (error) function normalize(name: string) { return name.trim().toLowerCase(); } return names.map(normalize); }

The rule checks arrow functions by default. Set checkArrowFunctions: false in the rule options to restrict diagnostics to function declarations and function expressions. Functions that capture an enclosing binding, lexical this, arguments, super, or a private name remain in their current scope.

unicorn/consistent-template-literal-escape

Enforce the \${ spelling over $\{ when escaping ${ in template literals. Both spellings cook to the same text, so the rule canonicalizes on the dollar escape and the autofix rewrites $\{ and \$\{ in place. Tagged templates are skipped because the tag function observes the raw text.

Example:

// reports: unicorn/consistent-template-literal-escape (error) const template = `Use \${name}, not $\{name}.`;

unicorn/custom-error-definition

Require user-defined Error subclasses to set name, call super(message), and assign their stack correctly.

Example:

// reports: unicorn/custom-error-definition (error) class MyError extends Error { constructor() { void 0; } }

unicorn/empty-brace-spaces

Reject whitespace inside empty {} braces.

Example:

// reports: unicorn/empty-brace-spaces (error) const o = {};

unicorn/error-message

Require a non-empty message argument when constructing a built-in Error.

Example:

// reports: unicorn/error-message (error) throw new Error();

unicorn/escape-case

Require consistent case for escape sequences (\xA9 over \xa9, \u00B5 over \u00b5).

Escapes are read from the raw source of string literals and of every template segment, including the segments of an interpolated template. An escaped backslash opens no escape, so "\\xa9" is left alone, and an escape ends after its digits, so the letters trailing the canonical "\x41bcd" are not part of it. Tagged templates are skipped because the tag function observes the raw text.

Example:

// reports: unicorn/escape-case (error) const s = "\xa9";

unicorn/expiring-todo-comments

Require every TODO/FIXME/XXX comment to declare an expiration date or package version.

Example:

// reports: unicorn/expiring-todo-comments (error) // TODO: remove the legacy branch after migration. export const legacyMode = true;

unicorn/explicit-length-check

Require explicit comparison of .length / .size instead of relying on truthy coercion.

Example:

const xs = [1, 2, 3]; if ( // reports: unicorn/explicit-length-check (error) xs.length ) { void 0; }

unicorn/filename-case

Enforce a single case style (kebab / camel / snake / pascal) for source filenames and directory names.

Every segment of a file’s project-relative path is checked — directories first, then the filename, then a lowercase check on the file extension — and the first offending segment produces the file’s single diagnostic with concrete rename samples. Files outside the project directory are judged by basename alone. $-prefixed segments, the default index.js / index.mjs / index.cjs / index.ts / index.tsx / index.vue basenames, and characters outside [a-zA-Z0-9_-] are exempt from case conversion.

The option object supports case (one enforced style: kebabCase — the default — camelCase, camelCaseWithAcronyms, snakeCase, or pascalCase), cases (a map of allowed styles; case and cases are mutually exclusive, and enabling none falls back to kebab-case), ignore (regular-expression strings matched against each path segment), multipleFileExtensions (default true: only the stem before the first dot is checked), and checkDirectories (default true).

Example:

// File: src/UserProfile.ts // reports: unicorn/filename-case (error) // Filename is not in kebab case. Rename it to `user-profile.ts`. export function renderUserProfile() {}
export default { rules: { "unicorn/filename-case": [ "error", { cases: { kebabCase: true, pascalCase: true }, ignore: ["^vendor-"], }, ], }, };

unicorn/import-style

Restrict each module’s allowed import styles (named only, default only, namespace only).

Every reference to a configured module — static import, dynamic import(), require(…), and (opt-in) export … from — is classified into the styles it actually uses (unassigned, default, namespace, named) and reported when any of them falls outside the module’s allowed set. node:-prefixed specifiers inherit the bare module name’s policy. The built-in table allows only default imports of chalk and path and only named imports of util.

The option object supports styles (per-module maps of style-name booleans, or false to lift a module’s restrictions), extendDefaultStyles, and the checkImport, checkDynamicImport, checkExportFrom, and checkRequire toggles. checkExportFrom defaults to false; the other toggles default to true. For require, an allowed default style also accepts whole-object bindings because CommonJS offers no interop that distinguishes them.

Example:

// reports: unicorn/import-style (error) — use named import for module `util` import util from "node:util";
export default { rules: { "unicorn/import-style": [ "error", { styles: { lodash: { named: true }, react: { default: true, named: true }, path: false, }, }, ], }, };

unicorn/isolated-functions

Reject references to outer-scope variables (and this / super) inside functions that run outside their defining context. Functions are recognized as isolated when passed to a configured name (makeSynchronous and workerize by default), to browser.execute / page.evaluate, as the func property of chrome.scripting.executeScript / browser.scripting.executeScript, or when preceded by an @isolated comment; the functions, selectors, comments, and overrideGlobals options tune this.

Example:

const prefix = "user:"; // `makeSynchronous` runs the callback in a separate context, so the captured // `prefix` is unavailable there. // reports: unicorn/isolated-functions (error) const build = makeSynchronous(() => `${prefix}ready`);

unicorn/new-for-builtins

Require new when calling builtin constructors like Error, Map, Set, Date, and forbid new on primitive wrappers like String, Number, Boolean.

Example:

// reports: unicorn/new-for-builtins (error) const xs = Array(3);

unicorn/no-abusive-eslint-disable

Require every eslint-disable* directive to name the rules it disables.

Example:

// reports: unicorn/no-abusive-eslint-disable (error) // eslint-disable-next-line const _x = 0;

unicorn/no-accessor-recursion

Reject recursive reads on this.<prop> inside the getter / setter for <prop>.

Example:

class C { get value() { // reports: unicorn/no-accessor-recursion (error) return this.value; } }

unicorn/no-anonymous-default-export

Require a name on every default-exported function, class, or object.

Example:

// reports: unicorn/no-anonymous-default-export (error) export default function () { return 1; }

unicorn/no-array-callback-reference

Reject passing a function reference directly as the callback to map / filter / forEach / etc., which silently leaks extra index/array arguments to the callee.

Example:

function isEven(n: number) { return n % 2 === 0; } // reports: unicorn/no-array-callback-reference (error) const evens = [1, 2, 3].filter(isEven);

unicorn/no-array-for-each

Prefer for...of over Array.prototype.forEach.

Example:

// reports: unicorn/no-array-for-each (error) [1, 2, 3].forEach((x) => { console.log(x); });

unicorn/no-array-method-this-argument

Reject the second thisArg argument to array methods; use an explicit closure instead.

Example:

// reports: unicorn/no-array-method-this-argument (error) [1, 2].forEach( function (x) { console.log(this, x); }, { tag: "ctx" }, );

unicorn/no-array-reduce

Reject Array#reduce / Array#reduceRight in favor of explicit loops or other helpers.

Example:

// reports: unicorn/no-array-reduce (error) const total = [1, 2, 3].reduce((a, b) => a + b, 0);

unicorn/no-array-reverse

Prefer Array#toReversed over the mutating Array#reverse.

Example:

// reports: unicorn/no-array-reverse (error) const r = [1, 2, 3].reverse();

unicorn/no-array-sort

Prefer Array#toSorted over the mutating Array#sort.

Example:

// reports: unicorn/no-array-sort (error) const s = [3, 1, 2].sort();

unicorn/no-await-expression-member

Reject member access on an await expression without parens; require (await x).y.

Example:

async function f() { // reports: unicorn/no-await-expression-member (error) return (await Promise.resolve({ a: 1 })).a; }

unicorn/no-await-in-promise-methods

Reject await inside arrays passed to Promise.all / Promise.allSettled / Promise.race / Promise.any, the awaits serialize the calls.

Example:

async function f() { // reports: unicorn/no-await-in-promise-methods (error) await Promise.all([await Promise.resolve(1), Promise.resolve(2)]); }

unicorn/no-console-spaces

Reject leading or trailing spaces in arguments to console.log and friends, console already inserts spaces between args.

Example:

// reports: unicorn/no-console-spaces (error) console.log("hello ", "world");

Reject direct reads or assignments to document.cookie; use the Cookie Store API or a wrapper.

Example:

// reports: unicorn/no-document-cookie (error) document.cookie = "name=value";

unicorn/no-empty-file

Reject source files whose only content is whitespace and/or comments.

Example:

// reports: unicorn/no-empty-file (error) ;

unicorn/no-for-loop

Prefer for...of over index-based for loops over arrays.

Example:

const xs = [1, 2, 3]; // reports: unicorn/no-for-loop (error) for (let i = 0; i < xs.length; i++) { void xs[i]; }

unicorn/no-hex-escape

Prefer Unicode escape (\u00A9) over hexadecimal escape (\xA9).

Escapes are read from the raw source of string literals and of every template segment, including the segments of an interpolated template. An escaped backslash opens no escape, so "\\x64" is left alone. Tagged templates are skipped because the tag function observes the raw text.

Example:

// reports: unicorn/no-hex-escape (error) const s = "\xA9";

unicorn/no-immediate-mutation

Reject mutating a value on the same expression that produces it ([...x].push(y)); separate the construction and the mutation.

Example:

// reports: unicorn/no-immediate-mutation (error) const last = [1, 2, 3].push(4);

unicorn/no-instanceof-builtins

Reject instanceof Array, instanceof Error, instanceof Map, etc., they fail across realms and for subclasses.

Example:

const x: unknown = []; // reports: unicorn/no-instanceof-builtins (error) if (x instanceof Array) { void x; }

unicorn/no-invalid-fetch-options

Reject GET / HEAD fetch() calls that also set a request body, which throws at runtime.

Example:

// reports: unicorn/no-invalid-fetch-options (error) fetch("https://example.com", { method: "GET", body: "x" });

unicorn/no-invalid-remove-event-listener

Reject removeEventListener calls whose handler argument is a fresh function reference and therefore matches no registered listener.

Example:

const el = new EventTarget(); // reports: unicorn/no-invalid-remove-event-listener (error) el.removeEventListener("click", () => {});

unicorn/no-keyword-prefix

Reject identifiers that start with a reserved word (newFoo, classBar).

Example:

// reports: unicorn/no-keyword-prefix (error) const newFoo = 1;

unicorn/no-lonely-if

Reject if as the only statement inside an else block; use else if instead.

Example:

if (1 === 1) { void 0; } else { // reports: unicorn/no-lonely-if (error) if (2 === 2) { void 0; } }

unicorn/no-magic-array-flat-depth

Reject magic-number depth arguments to Array#flat; require Infinity or a named constant.

Example:

// reports: unicorn/no-magic-array-flat-depth (error) const flat = [1, [2, [3]]].flat(2);

unicorn/no-named-default

Reject re-importing or re-exporting a default binding under a name that differs from the upstream binding.

Example:

// reports: unicorn/no-named-default (error) import { default as React } from "react";

unicorn/no-negated-condition

Reject negated conditions in if/else and ternaries when the positive form is shorter.

Example:

const x = 1; // reports: unicorn/no-negated-condition (error) if (x !== 0) { void "nonzero"; } else { void "zero"; }

unicorn/no-negation-in-equality-check

Reject !a === b; require a !== b or !(a === b).

Example:

const a = 1; const b = 2; // reports: unicorn/no-negation-in-equality-check (error) const eq = !a === b;

unicorn/no-nested-ternary

Reject ternaries nested inside other ternaries.

Example:

const x = 1; // reports: unicorn/no-nested-ternary (error) const r = x === 0 ? "zero" : x > 0 ? "pos" : "neg";

unicorn/no-new-array

Reject the new Array(...) constructor; use array literals or Array.from / Array.of.

Example:

// reports: unicorn/no-new-array (error) const a = new Array(3);

unicorn/no-new-buffer

Reject the deprecated new Buffer() constructor; use Buffer.from or Buffer.alloc.

Example:

// reports: unicorn/no-new-buffer (error) const b = new Buffer(10);

unicorn/no-null

Reject the null literal in favor of undefined.

Example:

// reports: unicorn/no-null (error) const x = null;

unicorn/no-object-as-default-parameter

Reject inline object literals as default values for function parameters.

Example:

// reports: unicorn/no-object-as-default-parameter (error) function f(opts = { tag: "default" }) { void opts; }

unicorn/no-process-exit

Reject process.exit(); throw or return a non-zero status instead.

Example:

// reports: unicorn/no-process-exit (error) process.exit(1);

unicorn/no-single-promise-in-promise-methods

Reject Promise.all / Promise.race / etc. called with a single-element array; the wrapper is redundant.

Example:

// reports: unicorn/no-single-promise-in-promise-methods (error) const p = Promise.all([Promise.resolve(1)]);

unicorn/no-static-only-class

Reject classes whose every member is static; use a plain module-level namespace instead.

Example:

// reports: unicorn/no-static-only-class (error) class Utility { static helper() { return 42; } }

unicorn/no-thenable

Reject defining a property named then on objects, modules, or classes, await and Promise resolution accidentally invoke it.

Example:

const o = { // reports: unicorn/no-thenable (error) then() { return 1; }, };

unicorn/no-this-assignment

Reject const self = this and similar aliases; capture via arrow functions instead.

Example:

class C { m() { // reports: unicorn/no-this-assignment (error) const self = this; return self; } }

unicorn/no-typeof-undefined

Reject typeof x === "undefined"; compare against undefined directly.

Only a typeof on the left of an equality comparison (===, ==, !==, !=) against the string literal "undefined" is reported. A reversed "undefined" === typeof x, a template-literal operand (typeof x === `undefined`), and a global operand are all left alone, matching upstream: rewriting typeof window === "undefined" to window === undefined throws a ReferenceError when the global is undeclared. A global here means an identifier that resolves to no local binding. Enable checkGlobalVariables to also check global operands, which are then offered an opt-in suggestion rather than an automatic fix.

The autofix removes the typeof, upgrades == / != to their strict form, and replaces the literal, but declines rather than risk an incorrect edit when removing typeof could trigger Automatic Semicolon Insertion (the operand starts on a different line, or begins with a character that could merge with the preceding token).

Example:

let value: unknown; // reports: unicorn/no-typeof-undefined (error) typeof value === "undefined";

unicorn/no-unnecessary-array-flat-depth

Reject 1 as the explicit depth argument of Array#flat; the default is already 1.

Example:

// reports: unicorn/no-unnecessary-array-flat-depth (error) const flat = [1, [2]].flat(1);

unicorn/no-unnecessary-array-splice-count

Reject .length / Infinity as the deleteCount argument to splice / toSpliced; omit it to delete to the end.

Example:

const arr = [1, 2, 3]; // reports: unicorn/no-unnecessary-array-splice-count (error) arr.splice(0, arr.length);

unicorn/no-unnecessary-await

Reject await on non-thenable expressions.

Example:

async function f() { // reports: unicorn/no-unnecessary-await (error) const x = await 42; }

unicorn/no-unnecessary-polyfills

Reject polyfill imports for APIs already available in the project’s targeted Node / browser baseline.

Every static import, dynamic import(), and static require(…) of a bare specifier is matched against the polyfill packages tracked by core-js-compat (both the core-js / core-js-pure submodule entries and standalone packages like object-assign, es6-promise, array.prototype.flat, mdn-polyfills/*, polyfill-*). The import is reported only when every targeted runtime already ships the feature natively.

The target environments are resolved in the same order as the upstream rule:

  1. The targets option, when present — a Browserslist  query string, an array of queries, or a Browserslist targets object (for example { "node": "18" }). Query forms are resolved under the production environment.
  2. Otherwise, standard Browserslist config discovery from the linted file’s directory: a browserslist / .browserslistrc file, a package.json browserslist field or section, custom browserslist-stats.json, and the BROWSERSLIST* environment overrides.
  3. Otherwise, the engines field of the nearest package.json.

When no targets can be resolved the rule stays silent. Queries that need inputs the native lint host cannot supply (extends <pkg>, supports <feature>, regional in XX usage, baseline …, and current node) are treated the same way and leave the rule silent for that file. The compatibility dataset is generated from pinned upstream package versions; see packages/lint/tools/polyfilldata/generate.mjs for provenance.

Example:

import "core-js/features/array/at"; // reports when every target already has Array#at
export default { rules: { "unicorn/no-unnecessary-polyfills": [ "error", { targets: { node: "18" }, }, ], }, };

unicorn/no-unnecessary-slice-end

Reject .length / Infinity as the end argument to slice; omit it to slice to the end.

Example:

const arr = [1, 2, 3]; // reports: unicorn/no-unnecessary-slice-end (error) const c = arr.slice(0, arr.length);

unicorn/no-unreadable-array-destructuring

Reject destructuring patterns with long hole runs ([,,,,a]); use a named index instead.

Example:

// reports: unicorn/no-unreadable-array-destructuring (error) const [, , , , a] = [1, 2, 3, 4, 5];

unicorn/no-unreadable-iife

Reject IIFEs whose nesting (multiple parens, arrow IIFE arguments) is hard to read.

Example:

// reports: unicorn/no-unreadable-iife (error) const r = (() => Math.random())();

unicorn/no-unused-properties

Reject object properties that are never read after definition.

The rule follows every reference to a variable bound to an object literal (or annotated with an inline {...} type, including function parameters) and reports the properties no reference can reach. Static accesses must name the key, destructuring must bind it, and matching references recurse into nested objects. Any escape — aliasing, passing, returning, spreading, exporting, mutating, or a dynamic key access — conservatively keeps every property.

Example:

const user = { id: "u_1", // reports: unicorn/no-unused-properties (error) debugLabel: "local-only", }; console.log(user.id);

unicorn/no-useless-collection-argument

Reject useless initializer arguments (new Set(), new Map([]), new Set(undefined)) on collection constructors.

Example:

// reports: unicorn/no-useless-collection-argument (error) const s = new Set([]);

unicorn/no-useless-error-capture-stack-trace

Reject Error.captureStackTrace(this, constructor) when the surrounding subclass relies on the default Error capture.

Example:

class MyError extends Error { constructor(msg: string) { super(msg); // reports: unicorn/no-useless-error-capture-stack-trace (error) Error.captureStackTrace(this, MyError); } } void new MyError("x");

unicorn/no-useless-fallback-in-spread

Reject ...(x ?? {}) and similar fallbacks when spreading; the spread of null / undefined is already a no-op.

Example:

const x: { a: number } | null = null; // reports: unicorn/no-useless-fallback-in-spread (error) const o = { ...(x ?? {}) };

unicorn/no-useless-iterator-to-array

Reject [...iterator] / Array.from(iterator) when the iterator can be consumed directly (e.g., inside for...of).

Example:

const arr = [1, 2]; // reports: unicorn/no-useless-iterator-to-array (error) for (const e of [...arr.entries()]) { void e; }

unicorn/no-useless-length-check

Reject arr.length checks that the iteration method itself already handles.

Example:

const xs = [1, 2, 3]; // reports: unicorn/no-useless-length-check (error) const hasPositive = xs.length > 0 && xs.some((x) => x > 0);

unicorn/no-useless-promise-resolve-reject

Reject return Promise.resolve(x) / return Promise.reject(e) inside async functions, return x and throw e work identically.

Example:

async function f() { // reports: unicorn/no-useless-promise-resolve-reject (error) return Promise.resolve(1); }

unicorn/no-useless-spread

Reject spreading a single iterable into a new collection of the same kind ([...arr], {...obj}) when the original would suffice.

Example:

// reports: unicorn/no-useless-spread (error) const a = [...[1, 2, 3]];

unicorn/no-useless-switch-case

Reject case clauses with an empty body that immediately precede a default whose body executes for them.

Example:

const x = 2; switch (x) { case 1: void 0; break; // reports: unicorn/no-useless-switch-case (error) case 2: default: void 0; }

unicorn/no-useless-undefined

Reject explicit undefined returns, default initializers, and arguments where the omission has the same meaning.

Example:

function f() { // reports: unicorn/no-useless-undefined (error) return undefined; }

unicorn/no-zero-fractions

Reject 1.0 / 1. / .5e0 in favor of 1, 1, and 0.5.

Example:

// reports: unicorn/no-zero-fractions (error) const n = 1.0;

unicorn/number-literal-case

Enforce one consistent case for every letter of a numeric literal: a lowercase radix prefix (0x, 0b, 0o), uppercase hex digits, and a lowercase exponent (0xFF over 0xff, 1e10 over 1E10). BigInt literals are covered too, and their n suffix stays lowercase (0xFFn). The autofix rewrites the literal in place. Only letter case changes, so the value is preserved.

Example:

// reports: unicorn/number-literal-case (error) const n = 1E10;

unicorn/numeric-separators-style

Enforce _ separator grouping (every 3 digits for decimal, every 4 for hex) in numeric literals.

Example:

// reports: unicorn/numeric-separators-style (error) const big = 1_2345;

unicorn/prefer-add-event-listener

Prefer addEventListener / removeEventListener over assigning to on* properties.

Example:

const el = document.querySelector("button")!; // reports: unicorn/prefer-add-event-listener (error) el.onclick = () => {};

unicorn/prefer-array-find

Prefer Array#find / Array#findLast over filter(...)[0] / filter(...).at(-1).

Example:

const xs = [1, 2, 3]; // reports: unicorn/prefer-array-find (error) const first = xs.filter((x) => x > 1)[0];

unicorn/prefer-array-flat

Prefer Array#flat over legacy flattening idioms ([].concat(...arrs), reduce with concat).

Example:

// reports: unicorn/prefer-array-flat (error) const flat = [].concat([1, 2], [3, 4]);

unicorn/prefer-array-flat-map

Prefer Array#flatMap over map(...).flat().

Example:

// reports: unicorn/prefer-array-flat-map (error) const result = [1, 2].map((x) => [x, x]).flat();

unicorn/prefer-array-index-of

Prefer indexOf / lastIndexOf over findIndex / findLastIndex when matching by ===.

Example:

const xs = [1, 2, 3]; // reports: unicorn/prefer-array-index-of (error) const i = xs.findIndex((x) => x === 2);

unicorn/prefer-array-some

Prefer Array#some over filter(...).length > 0, find(...) !== undefined, and similar shapes.

Example:

const xs = [1, 2, 3]; // reports: unicorn/prefer-array-some (error) const hasLarge = xs.filter((x) => x > 1).length > 0;

unicorn/prefer-at

Prefer Array#at / String#at over negative index arithmetic when the indexed value and the .length receiver are the same runtime reference. Receiver mismatches are left unchanged because .at(-N) would select a different element.

Example:

const xs = [1, 2, 3]; // reports: unicorn/prefer-at (error) const last = xs[xs.length - 1]; const limits = [0, 1]; const unchanged = xs[limits.length - 1];

unicorn/prefer-bigint-literals

Prefer 1n over BigInt(1) and BigInt("1").

Example:

// reports: unicorn/prefer-bigint-literals (error) const big = BigInt(1);

unicorn/prefer-blob-reading-methods

Prefer Blob#arrayBuffer() / Blob#text() over FileReader#readAsArrayBuffer / readAsText.

Example:

const reader = new FileReader(); const blob = new Blob(["hello"]); // reports: unicorn/prefer-blob-reading-methods (error) reader.readAsArrayBuffer(blob);

unicorn/prefer-class-fields

Prefer class field declarations over constructor assignments to this.field = value.

Example:

class C { field: number; constructor() { // reports: unicorn/prefer-class-fields (error) this.field = 1; } }

unicorn/prefer-classlist-toggle

Prefer Element#classList.toggle(name, condition) over manual add / remove branches.

Example:

const el = document.createElement("button"); const cond = true; // reports: unicorn/prefer-classlist-toggle (error) if (cond) { el.classList.add("active"); } else { el.classList.remove("active"); }

unicorn/prefer-code-point

Prefer String#codePointAt / String.fromCodePoint over charCodeAt / fromCharCode.

Example:

// reports: unicorn/prefer-code-point (error) const code = "a".charCodeAt(0);

unicorn/prefer-date-now

Prefer Date.now() over new Date().getTime() / +new Date().

Example:

// reports: unicorn/prefer-date-now (error) const t = new Date().getTime();

unicorn/prefer-default-parameters

Prefer default parameter syntax over x = x ?? default reassignments inside the function body.

Example:

function f(name?: string) { // reports: unicorn/prefer-default-parameters (error) name = name ?? "guest"; return name; }

unicorn/prefer-dom-node-append

Prefer Node#append over Node#appendChild.

Example:

const parent = document.createElement("div"); const child = document.createElement("span"); // reports: unicorn/prefer-dom-node-append (error) parent.appendChild(child);

unicorn/prefer-dom-node-dataset

Prefer Element#dataset over getAttribute / setAttribute for data-* attributes.

Example:

const el = document.createElement("div"); // reports: unicorn/prefer-dom-node-dataset (error) el.getAttribute("data-user-id");

unicorn/prefer-dom-node-remove

Prefer ChildNode#remove over parent.removeChild(child).

Example:

const parent = document.createElement("div"); const child = document.createElement("span"); // reports: unicorn/prefer-dom-node-remove (error) parent.removeChild(child);

unicorn/prefer-dom-node-text-content

Prefer Node#textContent over HTMLElement#innerText.

Example:

const el = document.createElement("div"); // reports: unicorn/prefer-dom-node-text-content (error) el.innerText;

unicorn/prefer-event-target

Prefer EventTarget over Node’s EventEmitter when the code is shared between Node and the browser.

Example:

import { EventEmitter } from "node:events"; // reports: unicorn/prefer-event-target (error) const em = new EventEmitter();

unicorn/prefer-export-from

Prefer export ... from over importing-then-re-exporting in two statements.

Example:

import { useState } from "react"; // reports: unicorn/prefer-export-from (error) export { useState };

unicorn/prefer-global-this

Prefer globalThis over window, self, and global.

Example:

// reports: unicorn/prefer-global-this (error) const root = window;

unicorn/prefer-import-meta-properties

Prefer import.meta.dirname / import.meta.filename over fileURLToPath workarounds.

Example:

import { fileURLToPath } from "node:url"; // reports: unicorn/prefer-import-meta-properties (error) const filename = fileURLToPath(import.meta.url);

unicorn/prefer-includes

Prefer String#includes / Array#includes over indexOf(...) !== -1 and some(x => x === target).

Example:

const arr = [1, 2, 3]; // reports: unicorn/prefer-includes (error) const found = arr.indexOf(2) !== -1;

unicorn/prefer-json-parse-buffer

Prefer passing a Buffer directly to JSON.parse (Node 21+) instead of decoding to a string first.

Example:

const buf = Buffer.from('{"ok":true}'); // reports: unicorn/prefer-json-parse-buffer (error) const data = JSON.parse(buf.toString());

unicorn/prefer-keyboard-event-key

Prefer KeyboardEvent#key over the deprecated KeyboardEvent#keyCode / charCode / which.

Example:

const event = new KeyboardEvent("keydown"); // reports: unicorn/prefer-keyboard-event-key (error) const code = event.keyCode;

unicorn/prefer-logical-operator-over-ternary

Prefer a || b / a ?? b over the equivalent ternary a ? a : b.

Example:

const x: number | undefined = 1; // reports: unicorn/prefer-logical-operator-over-ternary (error) const y = x ? x : 0;

unicorn/prefer-math-min-max

Prefer Math.min / Math.max over ternaries computing the same value.

Example:

const a = 1; const b = 2; // reports: unicorn/prefer-math-min-max (error) const m = a < b ? a : b;

unicorn/prefer-math-trunc

Prefer Math.trunc over ~~x / x | 0 for integer truncation.

Example:

// reports: unicorn/prefer-math-trunc (error) const i = ~~3.7;

unicorn/prefer-modern-dom-apis

Prefer before / after / replaceWith over insertBefore / replaceChild / insertAdjacentText.

Example:

const parent = document.createElement("div"); const ref = document.createElement("span"); const node = document.createElement("strong"); // reports: unicorn/prefer-modern-dom-apis (error) parent.insertBefore(node, ref);

unicorn/prefer-modern-math-apis

Prefer Math.log10 / Math.hypot / Math.log2 / Math.cbrt over their legacy approximations.

Example:

const x = 10; // reports: unicorn/prefer-modern-math-apis (error) const l = Math.log(x) * Math.LOG10E;

unicorn/prefer-module

Prefer ES modules (import / export) over CommonJS (require / module.exports / __dirname / __filename).

Example:

// reports: unicorn/prefer-module (error) require("path");

unicorn/prefer-native-coercion-functions

Prefer the bare String / Number / Boolean / BigInt functions over x => String(x) arrow wrappers.

Example:

// reports: unicorn/prefer-native-coercion-functions (error) const xs = ["1", "2"].map((x) => Number(x));

unicorn/prefer-negative-index

Prefer negative-index lookups (arr.at(-1), arr.slice(-2)) over arr.length - 1 / arr.length - 2 arithmetic.

Example:

const a = [1, 2, 3]; // reports: unicorn/prefer-negative-index (error) const tail = a.slice(a.length - 1);

unicorn/prefer-node-protocol

Prefer node:fs / node:path / etc. over the bare Node builtin specifier.

Example:

// reports: unicorn/prefer-node-protocol (error) import * as fs from "fs";

unicorn/prefer-number-properties

Prefer the ES2015 Number static members over their global counterparts: Number.isNaN, Number.isFinite, Number.parseInt, Number.parseFloat, and, when enabled, Number.NaN, Number.POSITIVE_INFINITY, and Number.NEGATIVE_INFINITY. The namespaced forms coerce more predictably and are easier to discover. Only a reference to the real global is reported, so a locally shadowed parseInt or isNaN is left alone, and a parseInt(value) or parseInt(value, 10) call (already base 10) is not flagged.

Example:

// reports: unicorn/prefer-number-properties (error) void isNaN(0); const options = { // reports: unicorn/prefer-number-properties (error) normalize: parseFloat, // reports: unicorn/prefer-number-properties (error) parseInt, };

NaN and Infinity are unchecked by default. Set checkNaN: true to flag NaN, and checkInfinity: true to flag Infinity (a negated -Infinity fixes to Number.NEGATIVE_INFINITY):

// lint.config.json rule entry "unicorn/prefer-number-properties": ["error", { "checkInfinity": true, "checkNaN": true }]

The pure aliases (parseInt, parseFloat, NaN, Infinity) autofix directly. isNaN and isFinite autofix only when their sole argument is a number, since Number.isNaN and Number.isFinite skip the coercion the global forms perform; otherwise the rewrite is offered as a suggestion.

unicorn/prefer-object-from-entries

Prefer Object.fromEntries over reduce-into-object patterns.

Example:

const entries: Array<[string, number]> = [["a", 1]]; // reports: unicorn/prefer-object-from-entries (error) const obj = entries.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});

unicorn/prefer-optional-catch-binding

Prefer catch { ... } over catch (binding) { ... } when the caught error is never referenced. The binding is resolved through the type checker, so any unused name reports and a name that only appears in a comment or string literal still counts as unused.

Example:

try { throw new Error("x"); // reports: unicorn/prefer-optional-catch-binding (error) } catch (e) { void 0; }

unicorn/prefer-prototype-methods

Prefer borrowing prototype methods (Array.prototype.slice.call) over [].slice.call empty-instance lookups.

Example:

// reports: unicorn/prefer-prototype-methods (error) const slice = [].slice;

unicorn/prefer-query-selector

Prefer Document#querySelector over getElementById, getElementsByClassName, and getElementsByTagName.

Example:

const doc = document; // reports: unicorn/prefer-query-selector (error) doc.getElementById("main");

unicorn/prefer-reflect-apply

Prefer Reflect.apply(fn, thisArg, args) over Function.prototype.apply.call(fn, thisArg, args).

Example:

function f(a: number, b: number) { return a + b; } // reports: unicorn/prefer-reflect-apply (error) const r = Function.prototype.apply.call(f, null, [1, 2]);

unicorn/prefer-regexp-test

Prefer RegExp#test over String#match / RegExp#exec when only a boolean is needed.

Example:

// reports: unicorn/prefer-regexp-test (error) if ("abc".match(/a/)) { void 0; }

unicorn/prefer-response-static-json

Prefer Response.json(value) over new Response( JSON.stringify(value), { headers: {"content-type": "application/json"} }).

Example:

// reports: unicorn/prefer-response-static-json (error) const r = new Response(JSON.stringify({ ok: true }));

unicorn/prefer-set-has

Prefer Set#has over Array#includes for repeated membership lookups against a constant collection.

Example:

const x = 2; // reports: unicorn/prefer-set-has (error) const found = [1, 2, 3].includes(x);

unicorn/prefer-set-size

Prefer Set#size over [...set].length and Array.from(set).length.

Example:

const s = new Set([1, 2, 3]); // reports: unicorn/prefer-set-size (error) const n = [...s].length;

unicorn/prefer-simple-condition-first

Prefer structurally simple conditions before complex conditions in boolean && / || chains. A simple condition is an identifier, a negated simple condition, or a strict comparison whose operands are identifiers, typeof identifiers, or literals. Value-producing logical expressions are not reported.

The fixer uses a stable partition, retaining the relative order of all simple conditions and of all complex conditions. It fixes only when every crossed complex condition is safe to move and no comment or TypeScript wrapper owns the affected logical syntax; otherwise the rule reports a diagnostic and leaves short-circuit behavior for manual review.

This rule has no options.

Illustrative shape:

function isAllowed(user: { role: string }) { return user.role === "admin"; } const user = { role: "admin" }; const featureEnabled = true; // reports: unicorn/prefer-simple-condition-first (error) if (isAllowed(user) && featureEnabled) { console.log("enabled"); } // fixed form if (featureEnabled && isAllowed(user)) { console.log("enabled"); }

unicorn/prefer-single-call

Prefer a single push / unshift / classList.add / addEventListener with multiple arguments over consecutive single-argument calls.

Example:

const xs: number[] = []; xs.push(1); // reports: unicorn/prefer-single-call (error) xs.push(2);

unicorn/prefer-spread

Prefer spread ([...arr], [...str]) over Array.from, Array.prototype.slice.call, concat([]), and split('').

Example:

const a = [1, 2, 3]; // reports: unicorn/prefer-spread (error) const b = Array.from(a);

unicorn/prefer-string-raw

Prefer String.raw for path literals and other strings that would otherwise need backslash escapes.

The rule only reports when a String.raw template would reproduce the value exactly, so a literal is left alone when it carries any escape other than \\ ("\\d\t" would turn its tab into the two characters \t), when its value ends with a backslash (a raw template cannot end in one), when it contains a backtick or ${ (the template would close early or interpolate), or when a line continuation spreads it over two lines. A no-substitution template is reported under the same value-preserving condition, except that its newlines are already literal, so a multi-line template stays reportable; the quasi of a tagged template — including String.raw`…` itself — is never reported. The report carries no autofix.

Example:

// reports: unicorn/prefer-string-raw (error) const p = "C:\\Users\\me"; // not reported: the tab escape would not survive String.raw const q = "C:\\Users\tme";

unicorn/prefer-string-replace-all

Prefer String#replaceAll(literal, replacement) over replace(/literal/g, replacement).

Example:

// reports: unicorn/prefer-string-replace-all (error) const out = "abc".replace(/a/g, "x");

unicorn/prefer-string-slice

Prefer String#slice over the deprecated substr / substring.

Example:

// reports: unicorn/prefer-string-slice (error) const s = "hello".substr(0, 3);

unicorn/prefer-string-starts-ends-with

Prefer String#startsWith / String#endsWith over equivalent RegExp#test and slice-then-compare idioms.

Example:

const s = "https://example.com"; // reports: unicorn/prefer-string-starts-ends-with (error) const b = s.slice(0, 4) === "http";

unicorn/prefer-string-trim-start-end

Prefer String#trimStart / String#trimEnd over the deprecated trimLeft / trimRight.

Example:

// reports: unicorn/prefer-string-trim-start-end (error) const s = " hi ".trimLeft();

unicorn/prefer-structured-clone

Prefer structuredClone(x) over JSON.parse(JSON.stringify(x)) for deep cloning.

Example:

const original = { a: 1 }; // reports: unicorn/prefer-structured-clone (error) const clone = JSON.parse(JSON.stringify(original));

unicorn/prefer-switch

Prefer switch over chains of three or more else if clauses comparing the same discriminant.

Example:

const k = "a"; // reports: unicorn/prefer-switch (error) if (k === "a") { void 0; } else if (k === "b") { void 0; } else if (k === "c") { void 0; }

unicorn/prefer-ternary

Prefer a ternary over if / else whose two branches differ only in the right-hand side of a common assignment, return, or throw.

Example:

const cond = true; function f(): number { // reports: unicorn/prefer-ternary (error) if (cond) { return 1; } else { return 2; } }

unicorn/prefer-top-level-await

Prefer top-level await over .then / IIFE wrappers in ES modules.

Example:

async function load(): Promise<string> { return "ready"; } // reports: unicorn/prefer-top-level-await (error) load().then((value) => console.log(value));

unicorn/prefer-type-error

Require throwing TypeError (not a bare Error) when the surrounding if is a runtime type check.

Example:

function f(x: unknown) { if (typeof x !== "number") { // reports: unicorn/prefer-type-error (error) throw new Error("must be number"); } return x; }

unicorn/prevent-abbreviations

Apply the complete canonical replacement table to lexical bindings and their references, compound and cased names, and physical filenames.

The established unicorn/prevent-abbreviations ID is retained for configuration compatibility. Its behavior follows the final upstream version before eslint-plugin-unicorn renamed the rule to name-replacements in June 2026.

A binding is diagnosed once at its declaration. One collision-free replacement is autofixed across every reference, while ambiguous replacements are editor suggestions. Bindings declared with a named export modifier, plus ambient, cross-file-merged, JSX tag, .vue source, parameter-property, and attached-JSDoc @param bindings, remain diagnostic-only because a source-only rename cannot safely preserve their external names or references. A local binding re-exported through an export list remains fixable because the fixer preserves the external spelling (export { err } becomes export { error as err }). Default-export local names can likewise change without changing the external default name. The conservative .vue gate protects template references that are outside TypeScript’s source-file AST.

The option object supports replacements, extendDefaultReplacements, allowList, extendDefaultAllowList, ignore, checkVariables, checkProperties, checkFilenames, checkShorthandProperties, checkDefaultAndNamespaceImports, and checkShorthandImports. Import controls accept true, false, or "internal". The ignore option accepts regular-expression strings.

Example:

// reports once and fixes both occurrences to `button` const btn = document.querySelector("button"); btn?.focus();
export default { rules: { "unicorn/prevent-abbreviations": [ "error", { checkProperties: true, replacements: { cmd: { command: true }, ref: false, }, }, ], }, };

unicorn/relative-url-style

Enforce a single style (always leading ./ vs. never) for relative URLs passed to new URL.

Example:

const base = "https://example.com"; const u = new URL( // reports: unicorn/relative-url-style (error) "./dashboard", base, );

unicorn/require-array-join-separator

Require an explicit separator argument to Array#join instead of relying on the default ",".

Example:

// reports: unicorn/require-array-join-separator (error) const s = [1, 2, 3].join();

unicorn/require-module-attributes

Require non-empty with / assert options on import / export statements that use them at all.

Example:

// reports: unicorn/require-module-attributes (error) import data from "./data.json";

unicorn/require-module-specifiers

Require a non-empty specifier list on every import / export statement.

Example:

// reports: unicorn/require-module-specifiers (error) import "./side-effect.js";

unicorn/require-number-to-fixed-digits-argument

Require an explicit digits argument to Number#toFixed.

Example:

// reports: unicorn/require-number-to-fixed-digits-argument (error) const s = (1.234).toFixed();

unicorn/require-post-message-target-origin

Require an explicit targetOrigin argument to window.postMessage.

Example:

const win = window; // reports: unicorn/require-post-message-target-origin (error) win.postMessage({ kind: "ping" });

unicorn/string-content

Rewrite configured patterns inside string literals and template quasis (e.g., curly quotes for straight ones, ... into a real ellipsis).

The rule has no default patterns: a bare severity reports nothing. The options object maps regular-expression sources to replacements under patterns; the first configured pattern that matches a string wins, and every occurrence is replaced. A replacement is either the suggestion string or an object with suggest (required), fix (default true; false reports the rewrite as an opt-in editor suggestion), caseSensitive (default true), and message (custom diagnostic text with {{match}} / {{suggest}} placeholders).

String literals match on their cooked value and are re-quoted with escapes preserved; JSX attribute strings encode the delimiter quote as an HTML entity because they cannot use backslash escapes. Template quasis match on their raw text, keep substitutions untouched, and re-escape backticks and ${ introduced by a replacement; quasis under the gql / html / sql / svg tags and styled.* member tags are exempt. The selectors list replaces the default Literal / TemplateElement targets with AST selectors.

Illustrative shape:

// With `patterns: { unicorn: "🦄" }` configured: // reports: unicorn/string-content (error) const label = "unicorn";

unicorn/switch-case-braces

Enforce a consistent presence/absence of {} braces around case clauses inside switch.

Example:

const k = "a"; switch (k) { // reports: unicorn/switch-case-braces (error) case "a": void 0; break; }

unicorn/switch-case-break-position

Require a terminating break, continue, return, or throw to sit inside a case clause’s sole block instead of immediately after it. break and continue are automatically moved when comments and layout make that safe; return and throw remain diagnostic-only because moving their expressions can change block-scoped binding resolution.

Illustrative shape:

const state = "idle"; function reset(): void {} switch (state) { case "idle": { reset(); } break; }

unicorn/template-indent

Normalize whitespace-insensitive multiline template bodies relative to the source line containing the opening backtick. Substitutions, nested expressions, raw escapes, blank lines, and the source line-ending style are preserved.

By default, the rule checks the outdent, dedent, gql, sql, html, and styled tags; template arguments passed directly to dedent or stripIndent; templates preceded by /* HTML */ or /* indent */; and Jest inline snapshots. The options object can replace the tags, functions, comments, and selectors lists. indent accepts either a positive number of spaces or an exact non-empty whitespace string such as "\t".

Illustrative shape:

function sql(strings: TemplateStringsArray): string { return strings.join(""); } const query = sql` SELECT * FROM users WHERE active = true `;

unicorn/text-encoding-identifier-case

Enforce a canonical case for text-encoding identifiers — the dash-less "utf8" by default (so "UTF-8", "utf-8", and "Utf8" all report toward "utf8").

Only utf-8 / utf8 and ascii are recognized; every other label ("latin1", "UTF-16LE", …) passes through untouched. ascii is always the lowercase form. The dashed WHATWG spelling "utf-8" is enforced instead of the default in the contexts that echo the label back verbatim: the first argument of new TextDecoder(...), a JSX <meta charset> attribute, and a JSX <form accept-charset> attribute — or everywhere when the withDash: true option is set.

Only the encoding argument of fs.readFile() / fs.readFileSync() is autofixed; every other position reports an opt-in editor suggestion instead.

Example:

// reports: unicorn/text-encoding-identifier-case (error) — prefer "utf8" const enc = "utf-8"; // reports: unicorn/text-encoding-identifier-case (error) — prefer "utf-8" here const decoder = new TextDecoder("utf8");

unicorn/throw-new-error

Require throw new Error(...) over throw Error(...).

A thrown call is reported when its callee names an error constructor: a plain identifier, or a non-computed property access whose property name matches ^(?:[A-Z][\da-z]*)*Error$ (capitalized words closed by Error). That covers the built-ins (TypeError) and user-defined classes alike (ValidationError, ns.HttpError), while getError(), fooError(), and computed access (ns["FooError"]()) stay silent. An optional-chained callee (Error?.(), ns?.FooError()) is never reported, because new cannot be applied to an optional chain.

The autofix inserts new in front of the call. It parenthesizes a callee whose object chain still exposes a call, because a new expression’s callee ends at the first argument list: throw getGlobalThis().Error() becomes throw new (getGlobalThis().Error)().

Example:

// reports: unicorn/throw-new-error (error) throw Error("oops"); // reports: unicorn/throw-new-error (error) throw ValidationError("bad"); // reports: unicorn/throw-new-error (error) throw ns.HttpError(404);
Last updated on