Rule catalogue
Every rule @hyperfrontend/linter ships, by family. A rule runs once a configuration block turns it on by its id, and a preset turns on every rule marked with it. Each rule's heading is a stable link target, so #typescriptno-namespace always finds typescript/no-namespace.
| Family | Rules | Fixable | Preset |
|---|---|---|---|
| Core rules | 48 | 5 | CODE_RULES |
| TypeScript rules | 26 | 9 | CODE_RULES |
| JSDoc rules | 32 | 8 | JSDOC_RULES |
| React rules | 19 | 2 | REACT_RULES |
| React hooks rules | 2 | 0 | REACT_RULES |
| package.json rules | 1 | 0 | |
| Convention rules | 28 | 11 |
🔧 marks a rule whose fixes --fix applies. Suggestions, for changes that need judgement, appear in the JSON report and are described under the rules that offer them.
Core rules
Reimplemented from ESLint's own rules. CODE_RULES turns on the ones eslint:recommended does.
| Rule | Reads | 🔧 | CODE_RULES |
|---|---|---|---|
for-direction | TypeScript, JavaScript | ✅ | |
no-async-promise-executor | TypeScript, JavaScript | ✅ | |
no-case-declarations | TypeScript, JavaScript | ✅ | |
no-compare-neg-zero | TypeScript, JavaScript | ✅ | |
no-cond-assign | TypeScript, JavaScript | ✅ | |
no-constant-binary-expression | TypeScript, JavaScript | ✅ | |
no-constant-condition | TypeScript, JavaScript | ✅ | |
no-control-regex | TypeScript, JavaScript | ✅ | |
no-debugger | TypeScript, JavaScript | ✅ | |
no-delete-var | TypeScript, JavaScript | ✅ | |
no-dupe-else-if | TypeScript, JavaScript | ✅ | |
no-duplicate-case | TypeScript, JavaScript | ✅ | |
no-empty | TypeScript, JavaScript | ✅ | |
no-empty-character-class | TypeScript, JavaScript | ✅ | |
no-empty-pattern | TypeScript, JavaScript | ✅ | |
no-empty-static-block | TypeScript, JavaScript | ✅ | |
no-ex-assign | TypeScript, JavaScript | ✅ | |
no-extra-boolean-cast | TypeScript, JavaScript | 🔧 | ✅ |
no-fallthrough | TypeScript, JavaScript | ✅ | |
no-global-assign | TypeScript, JavaScript | ✅ | |
no-invalid-regexp | TypeScript, JavaScript | ✅ | |
no-irregular-whitespace | TypeScript, JavaScript | ✅ | |
no-loss-of-precision | TypeScript, JavaScript | ✅ | |
no-misleading-character-class | TypeScript, JavaScript | ✅ | |
no-nonoctal-decimal-escape | TypeScript, JavaScript | ✅ | |
no-octal | TypeScript, JavaScript | ✅ | |
no-prototype-builtins | TypeScript, JavaScript | ✅ | |
no-regex-spaces | TypeScript, JavaScript | 🔧 | ✅ |
no-restricted-imports | TypeScript, JavaScript | ||
no-self-assign | TypeScript, JavaScript | ✅ | |
no-shadow-restricted-names | TypeScript, JavaScript | ✅ | |
no-sparse-arrays | TypeScript, JavaScript | ✅ | |
no-unexpected-multiline | TypeScript, JavaScript | ✅ | |
no-unsafe-finally | TypeScript, JavaScript | ✅ | |
no-unsafe-optional-chaining | TypeScript, JavaScript | ✅ | |
no-unused-labels | TypeScript, JavaScript | 🔧 | ✅ |
no-unused-private-class-members | TypeScript, JavaScript | ✅ | |
no-useless-assignment | TypeScript, JavaScript | ||
no-useless-backreference | TypeScript, JavaScript | ✅ | |
no-useless-catch | TypeScript, JavaScript | ✅ | |
no-useless-escape | TypeScript, JavaScript | ✅ | |
no-var | TypeScript, JavaScript | 🔧 | ✅ |
prefer-const | TypeScript, JavaScript | 🔧 | ✅ |
prefer-rest-params | TypeScript, JavaScript | ✅ | |
prefer-spread | TypeScript, JavaScript | ✅ | |
require-yield | TypeScript, JavaScript | ✅ | |
use-isnan | TypeScript, JavaScript | ✅ | |
valid-typeof | TypeScript, JavaScript | ✅ |
for-direction
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid a for loop whose update moves the counter away from the bound its test checks.
Rule Details
A loop such as for (let i = 0; i < 10; i--) never ends by its test, which is almost always a typo in the update. The rule reports a for loop whose test compares a counter with <, <=, > or >= and whose update moves that counter the wrong way: i++ or i--, or i += n and i -= n where the step is known without running the code (a literal, arithmetic on literals, or a variable assigned once with such a value). An unknown or zero step is not reported. The rule does not fix.
Reimplements ESLint's for-direction.
Options
This rule has no options.
Examples
❌ Incorrect
for (let i = 0; i < 10; i--) {}
for (let i = 10; i >= 0; i++) {}
const step = -1
for (let i = 0; i < 10; i += step) {}
✅ Correct
for (let i = 0; i < 10; i++) {}
for (let i = 10; i >= 0; i -= 2) {}
for (let i = 0; i < 10; i += step()) {}
no-async-promise-executor
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid an async function as the executor of new Promise(...).
Rule Details
An error thrown inside an async executor rejects the executor's own promise, not the one being constructed, so it is lost and the new promise never settles. An async executor also usually means the code awaits something it could have returned directly. The rule reports the async keyword of a function or arrow passed as the first argument to new Promise(...). The rule does not fix.
Reimplements ESLint's no-async-promise-executor.
Options
This rule has no options.
Examples
❌ Incorrect
const result = new Promise(async (resolve) => {
resolve(await load())
})
✅ Correct
const result = new Promise((resolve, reject) => {
load().then(resolve, reject)
})
const direct = load()
no-case-declarations
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid let, const, function and class declarations directly under a case or default clause.
Rule Details
A declaration directly under a case clause belongs to the whole switch block, so every clause can see it, but it is only initialised when its own clause runs. Reaching it from another clause throws, or, for a function, quietly works only after hoisting. Wrapping the clause body in braces gives the declaration a block of its own. The rule reports every let, const, using, function and class declaration that is a direct statement of a clause; var is left alone. The rule does not fix.
Reimplements ESLint's no-case-declarations.
Options
This rule has no options.
Examples
❌ Incorrect
switch (kind) {
case 'a':
const label = 'first'
break
default:
function fallback() {}
}
✅ Correct
switch (kind) {
case 'a': {
const label = 'first'
break
}
default: {
function fallback() {}
}
}
Limitations
The original offers a suggestion that wraps the clause body in braces; this rule reports without one.
no-compare-neg-zero
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid comparing a value against -0 with a comparison operator.
Rule Details
Every equality and relational operator treats -0 and 0 as the same value, so x === -0 is also true for 0 and never tells them apart. The rule reports a comparison (==, ===, !=, !==, <, <=, >, >=) with -0 written as either operand. To test for negative zero, use Object.is(x, -0). The rule does not fix.
Reimplements ESLint's no-compare-neg-zero.
Options
This rule has no options.
Examples
❌ Incorrect
if (x === -0) {
reset()
}
✅ Correct
if (Object.is(x, -0)) {
reset()
}
if (x === 0) {
reset()
}
no-cond-assign
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid an assignment as the test of a condition unless it is wrapped in its own parentheses.
Rule Details
if (user = find(id)) is usually a mistyped comparison. When the assignment is meant, wrapping it in a second pair of parentheses says so. The rule reports an assignment that is the whole test of an if, while, do...while, for or conditional (?:) expression and is not parenthesised on its own: two pairs for an if, a loop or a ternary, one pair for a for loop, whose head parentheses do not count. The rule does not fix.
Reimplements ESLint's no-cond-assign, in its default "except-parens" mode.
Options
This rule has no options. The original's string option is not read, so its "always" mode, which reports every assignment in a test even when parenthesised, is not available.
Examples
❌ Incorrect
if (user = find(id)) {
greet(user)
}
while (node = node.next) {}
✅ Correct
if ((user = find(id))) {
greet(user)
}
if (user === find(id)) {
greet(user)
}
Limitations
Only an assignment that is the whole test is checked, as in the original's default mode: one nested inside it, as in if (ready && (value = read())), is not reported.
no-constant-binary-expression
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid comparisons and logical expressions whose outcome does not depend on the value being tested.
Rule Details
Some expressions look like they test a value but always evaluate the same way, usually because of operator precedence or a misunderstanding of how ?? and === work. The rule reports:
- a
&&or||whose left side is always truthy or always falsy, and a??whose left side is never or always nullish, such as{ ...options } ?? fallback; - an
==,===,!=or!==againstnull,undefinedor a boolean whose result is fixed by the other operand's shape alone, such asa + b === nullor!value == null; - a strict comparison against an object, array, function or class created on the spot, which can never be equal to anything else, and a loose comparison of two such values.
Names bound to the global undefined, Boolean and the like are only treated as constant when they are not shadowed. The rule does not fix.
Reimplements ESLint's no-constant-binary-expression.
Options
This rule has no options.
Examples
❌ Incorrect
const value1 = +x == null
const value2 = { ...options } ?? fallback
const value3 = items === []
const value4 = !foo == null
✅ Correct
const value1 = x == null
const value2 = options ?? fallback
const value3 = items.length === 0
const value4 = foo == null
no-constant-condition
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid conditions that always hold or never do.
Rule Details
A test that cannot change, such as if (true) or while (1), is usually a leftover of debugging or a mistake that makes a branch dead. The rule reports a constant test of an if, a conditional (?:) expression, and a while, do...while or for loop. Constant means the value is known from the code alone: literals, object and function expressions, typeof results, and the like.
Two loop forms pass: while (true), the idiom for a loop that ends by break, and any constant loop that a yield inside it can suspend, since the consumer of the generator decides when it ends. A for (;;) loop has no test and is not reported. The rule does not fix.
Reimplements ESLint's no-constant-condition, in its default mode.
Options
This rule has no options. The original's checkLoops option is not read: every loop except while (true) is checked, the original's default.
Examples
❌ Incorrect
if (false) {
debug()
}
while (1) {
poll()
}
const label = typeof value ? 'set' : 'unset'
✅ Correct
if (ready) {
run()
}
while (true) {
if (poll()) break
}
function* ids() {
for (let id = 0; true; id++) yield id
}
no-control-regex
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid control characters (U+0000 to U+001F) in regular expressions.
Rule Details
Control characters are rarely what a pattern means to match, and a \x1f in a pattern is more often a typo or a copied byte than a deliberate test. The rule reports a regular expression literal, or a RegExp(...) or new RegExp(...) call with a string pattern, that holds a control character written as a \x or \u escape or as the raw character itself. The readable escapes such as \t and \n are left alone, and so are control escapes of the form \cX. The message lists the characters found. The rule does not fix.
RegExp is recognised when it names the global, is reached through globalThis, window, self or global, or is held in a constant.
Reimplements ESLint's no-control-regex.
Options
This rule has no options.
Examples
❌ Incorrect
const unitSeparator = /\x1f/
const nul = new RegExp('\\x00')
✅ Correct
const space = /\x20/
const tab = /\t/
const lineFeed = new RegExp('\\cJ')
no-debugger
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid debugger statements.
Rule Details
A debugger statement pauses execution whenever developer tools are open, which is useful while debugging and wrong in committed code. The rule reports every debugger statement. It does not fix: removing a statement belongs to the author, and a breakpoint in the editor does the same job without a change to the source.
Reimplements ESLint's no-debugger.
Options
This rule has no options.
Examples
❌ Incorrect
function total(items: number[]): number {
debugger
return items.reduce((sum, item) => sum + item, 0)
}
✅ Correct
function total(items: number[]): number {
return items.reduce((sum, item) => sum + item, 0)
}
no-delete-var
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid the delete operator on a variable.
Rule Details
delete removes properties from objects. Applied to a plain variable name it does nothing in sloppy-mode code and returns false, and in strict code, which includes every module, it is a syntax error. The rule reports a delete whose operand is a bare identifier. To clear a variable, assign undefined to it. The rule does not fix.
Reimplements ESLint's no-delete-var.
Options
This rule has no options.
Examples
❌ Incorrect
var cache = {}
delete cache
✅ Correct
const cache: Record<string, number> = { hits: 1 }
delete cache.hits
no-dupe-else-if
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid an else if branch whose condition repeats, or is covered by, an earlier condition of the same chain.
Rule Details
In an if/else if chain only the first matching branch runs, so a later branch whose condition an earlier one already decides can never run. That is almost always a copy-and-paste slip where a different condition was meant. The rule compares each branch's condition with the conditions before it, token by token, and understands || and &&: a branch is reported when every way its condition can hold is already caught above, so a && b after a is reported, and a || b after a and then b is reported. Operands are compared as written, without evaluating them. The rule does not fix.
Reimplements ESLint's no-dupe-else-if.
Options
This rule has no options.
Examples
❌ Incorrect
if (isAdmin) {
grant()
} else if (isEditor) {
review()
} else if (isAdmin) {
audit()
}
if (a) {
first()
} else if (a && b) {
second()
}
✅ Correct
if (isAdmin) {
grant()
} else if (isEditor) {
review()
} else if (isViewer) {
audit()
}
if (a && b) {
first()
} else if (a) {
second()
}
no-duplicate-case
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid two case labels in one switch that are written the same way.
Rule Details
A switch runs the first clause whose label matches, so a second clause with the same label can never be reached, which usually means a label was copied and not changed. The rule reports every clause whose label repeats an earlier label of the same switch, compared token by token, so case a.b: and case a . b: count as the same and case 1: and case 1.0: do not. The rule does not fix.
Reimplements ESLint's no-duplicate-case.
Options
This rule has no options.
Examples
❌ Incorrect
switch (kind) {
case 'file':
open()
break
case 'folder':
expand()
break
case 'file':
preview()
break
}
✅ Correct
switch (kind) {
case 'file':
open()
break
case 'folder':
expand()
break
case 'link':
follow()
break
}
no-empty
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid empty block statements and empty switch statements.
Rule Details
An empty block is usually unfinished code, or an error silently swallowed by an empty catch. The rule reports a block statement with no statements, such as the body of an if, a loop, a try or a catch, and a switch with no cases. A comment inside the braces says the emptiness is intended and the block is left alone. Function bodies are never reported. The rule does not fix.
Reimplements ESLint's no-empty.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
allowEmptyCatch | boolean | false | Whether an empty catch block passes. |
Examples
❌ Incorrect
if (ready) {
}
try {
run()
} catch (error) {}
switch (kind) {
}
✅ Correct
if (ready) {
// nothing to do until the next tick
}
try {
run()
} catch (error) {
report(error)
}
function noop(): void {}
✅ Correct, with { "allowEmptyCatch": true }
try {
run()
} catch {}
Limitations
The original offers a suggestion that adds an "empty" comment to the block; this rule reports without one.
no-empty-character-class
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid empty character classes ([]) in regular expression literals.
Rule Details
An empty character class matches no character at all, so the whole expression around it can never match. It is usually a class whose contents were lost, or an attempt at "any character", which is written [^]. The rule reports a regular expression literal holding an empty, non-negated class anywhere in its pattern, nested classes of the v flag included. The negated empty class [^] is left alone. The rule does not fix.
Reimplements ESLint's no-empty-character-class.
Options
This rule has no options.
Examples
❌ Incorrect
const never = /^abc[]/
const nested = /[a[]]/v
✅ Correct
const letters = /^abc[a-z]/
const anything = /^abc[^]/
const bracket = /\[]/
Limitations
Patterns passed to RegExp(...) as strings are not read, as in the original.
no-empty-pattern
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid empty destructuring patterns.
Rule Details
A pattern such as const {} = options or const { a: {} } = options binds no name at all. It is usually a typo for a default value, { a = {} }. The rule reports every empty object pattern and empty array pattern, in declarations, assignments and parameters alike. The rule does not fix.
Reimplements ESLint's no-empty-pattern.
Options
This rule has no options. The original's allowObjectPatternsAsParameters option is not read, so an empty object pattern as a parameter is always reported.
Examples
❌ Incorrect
const {} = options
const [] = items
const {
settings: {},
} = options
function start({}) {}
✅ Correct
const { settings = {} } = options
const [first] = items
function start({ port }: { port: number }) {}
no-empty-static-block
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid empty class static blocks, static {}.
Rule Details
A class static { } block with nothing in it does nothing, and is usually left over from removed initialisation code or an unfinished change. The rule reports a static block with no statements. A comment inside the braces says the emptiness is intended, and the block is then left alone. The rule does not fix.
Reimplements ESLint's no-empty-static-block.
Options
This rule has no options.
Examples
❌ Incorrect
class Registry {
static {}
}
✅ Correct
class Registry {
static entries: string[] = []
static {
Registry.entries.push('default')
}
}
class Placeholder {
static {
// intentionally empty
}
}
no-ex-assign
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid assigning to the parameter of a catch clause.
Rule Details
The catch parameter holds the exception that was thrown. Assigning to it replaces that exception, so whatever handles or rethrows it afterwards works with something else, and the original error is lost. The rule reports every write to a binding declared by a catch parameter, destructured ones included. To transform the error, store the result in a new variable. The rule does not fix.
Reimplements ESLint's no-ex-assign.
Options
This rule has no options.
Examples
❌ Incorrect
try {
run()
} catch (error) {
error = new Error('run failed')
throw error
}
✅ Correct
try {
run()
} catch (error) {
throw new Error('run failed', { cause: error })
}
no-extra-boolean-cast
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset CODE_RULES
Forbid !!value and Boolean(value) where the value is already read as a boolean.
Rule Details
The test of an if, a loop or a conditional expression, the operand of ! and the argument of Boolean() all convert their value to a boolean already, so a cast there adds nothing but noise. The rule reports a double negation (!!value) or a Boolean(...) call, with or without new, in one of those places.
The fix removes the cast and keeps the parentheses the bare value then needs: if (!!a) becomes if (a), !Boolean(a && b) becomes !(a && b), Boolean() becomes false. It is withheld when a comment inside the cast would be lost, or when the call spreads its argument or takes more than one.
Reimplements ESLint's no-extra-boolean-cast.
Options
This rule has no options. The original's enforceForInnerExpressions and enforceForLogicalOperands options are not read, so a cast that is an operand of &&, || or ?? inside a test is not reported.
Examples
❌ Incorrect
if (!!user) {
greet(user)
}
const missing = !Boolean(user)
while (Boolean(queue.length)) {
queue.pop()
}
✅ Correct
if (user) {
greet(user)
}
const present = !!user
const flag = Boolean(user)
no-fallthrough
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid a switch case from falling through into the next one without a comment saying so.
Rule Details
When the end of a case can be reached, control runs on into the next case. That is sometimes intended and far more often a forgotten break. The rule follows the code paths of each case and reports the next case when the end of the previous one is reachable: no break, return, throw or continue stops every path.
A comment says the fall-through is meant when it contains "falls through", "fall through" or "fallthrough", in any letter case, and is the last comment before the next case, or the last one inside a block that is the case's only statement. Linter directive comments do not count. An empty case directly above the next one, as in case 'a': case 'b':, is a shared label and passes; one separated from the next case by a line is reported. The rule does not fix.
Reimplements ESLint's no-fallthrough.
Options
This rule has no options. The original's options commentPattern, allowEmptyCase and reportUnusedFallthroughComment are not read.
Examples
❌ Incorrect
switch (kind) {
case 'warn':
log(kind)
case 'error':
alert(kind)
}
✅ Correct
switch (kind) {
case 'warn':
log(kind)
break
case 'error':
alert(kind)
}
switch (kind) {
case 'info':
case 'warn':
log(kind)
// falls through
case 'error':
alert(kind)
}
no-global-assign
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid assigning to read-only globals such as Object, undefined or window.
Rule Details
Overwriting a built-in global breaks every other piece of code that relies on it, and in strict code assigning to one such as undefined throws. The rule reports every write, an assignment, an update such as ++ or a destructuring target, to a global variable that is read-only: the language's built-ins, and the globals the configuration declares as read-only through its language settings. A local variable that shadows the global is not reported. The rule does not fix.
Reimplements ESLint's no-global-assign.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
exceptions | string[] | [] | Names of read-only globals that may be assigned all the same. |
Examples
❌ Incorrect
Object = null
undefined = 1
✅ Correct
let Object = null
const value = undefined
✅ Correct, with { "exceptions": ["Object"] }
Object = null
no-invalid-regexp
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid RegExp(...) calls with a pattern or flags the engine refuses.
Rule Details
A regular expression literal with a syntax error fails to parse, so the problem shows up at once. A pattern passed to RegExp as a string is only checked when the call runs, where it throws. The rule reads RegExp(...) and new RegExp(...) calls whose arguments are string literals and reports:
- flags the language does not know, a flag given twice, or
uandvtogether; - a pattern the engine refuses under the flags given, with the engine's reason in the message.
When the flags are not a string literal, the pattern is reported only if no choice of flags would accept it. RegExp is recognised when it names the global, is reached through the global object, or is held in a constant. The rule does not fix.
Reimplements ESLint's no-invalid-regexp.
Options
This rule has no options. The original's allowConstructorFlags option is not read, so a flag the language does not know is always reported.
Examples
❌ Incorrect
const unclosed = new RegExp('[')
const unknownFlag = RegExp('.', 'z')
const both = new RegExp('a', 'uv')
✅ Correct
const word = new RegExp('\\w+', 'g')
const dynamic = new RegExp(pattern, flags)
no-irregular-whitespace
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid whitespace other than spaces, tabs and line breaks outside string literals.
Rule Details
A no-break space, a zero-width space or a byte-order mark in the middle of a file looks like an ordinary space, or like nothing, in most editors, yet the engine does not always treat it as whitespace, and it can break a token or a diff in ways that are hard to see. Such characters usually arrive by copy and paste. The rule reports every run of them: form feed and vertical tab, U+0085, U+00A0, U+1680, U+180E, U+2000 to U+200B, U+2028, U+2029, U+202F, U+205F, U+3000 and U+FEFF. String literals may hold them on purpose and are skipped unless skipStrings is false. Comments, template literals and regular expressions are always read. The rule does not fix.
Reimplements ESLint's no-irregular-whitespace.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
skipStrings | boolean | true | Whether irregular whitespace inside string literals passes. |
The original's skipComments, skipRegExps, skipTemplates and skipJSXText options are not read; those places are always checked, the original's default.
Examples
The examples hold invisible characters: a zero-width space (U+200B) in the comment and a no-break space (U+00A0) in the strings.
❌ Incorrect
// the running total
const total = 1
❌ Incorrect, with { "skipStrings": false }
const label = 'a b'
✅ Correct
const total = 1
const label = 'a b'
no-loss-of-precision
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid number literals that a double-precision float cannot hold exactly as written.
Rule Details
JavaScript numbers are 64-bit floats with about 15 to 17 significant decimal digits. A literal with more precision than that, such as 9007199254740993, is silently stored as a nearby number, so the value the code uses is not the one the author wrote. The rule reports a number literal, in decimal, binary, octal or hexadecimal notation and with or without numeric separators, whose stored value no longer prints as the digits written. Trailing zeros and exponent notation that round-trip exactly pass. BigInt literals are never reported. The rule does not fix.
Reimplements ESLint's no-loss-of-precision.
Options
This rule has no options.
Examples
❌ Incorrect
const big = 9007199254740993
const long = 5123000000000000000000000000001
const precise = 1.230000000000000000000000000001
const hex = 0x20000000000001
✅ Correct
const big = 9007199254740991
const exact = 12345
const scaled = 1.23e30
const huge = 9007199254740993n
no-misleading-character-class
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid character classes holding characters that are written as one but made of several code points.
Rule Details
A character class matches one character at a time, so a symbol that is really a sequence does not match as written: the class matches each of its parts on their own. The rule reports a class in a regular expression literal, or in the string pattern of a RegExp(...) call, that holds:
- a surrogate pair, such as an emoji, without the
uorvflag; - a character followed by a combining mark, such as
Awith a combining accent; - an emoji followed by a skin-tone modifier;
- a flag written as two regional indicator symbols;
- a sequence joined with zero-width joiners, such as a family emoji.
Characters written as escapes are read the same as the characters themselves, and each problem is reported where it stands in the source. The rule does not fix.
Reimplements ESLint's no-misleading-character-class.
Options
This rule has no options. The original's allowEscape option is not read, so escaped sequences are always reported.
Examples
❌ Incorrect
const thumb = /^[👍]$/
const toned = /^[👍🏻]$/u
const flag = /^[🇯🇵]$/u
✅ Correct
const thumb = /^[👍]$/u
const either = /^(?:👍🏻|👎)$/u
Limitations
The original offers a suggestion that adds the u flag to a pattern whose only problem is a surrogate pair; this rule reports without one.
no-nonoctal-decimal-escape
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid the escapes \8 and \9 in string literals.
Rule Details
\8 and \9 look like octal escapes but escape nothing: in sloppy-mode code they stand for the digits 8 and 9, and in strict code they are a syntax error. They are usually a typo for an octal or a \x escape, or a backslash that was meant to be doubled. The rule reports each \8 and \9 in a string literal, skipping a backslash that is itself escaped ('\\8').
This linter's parser refuses \8 and \9 in JavaScript and TypeScript string literals, so such code is reported as a parsing error before any rule runs. The rule reports in the one place the parser lets them through: the quoted value of a JSX attribute, where backslashes are not escapes and the text is taken as written. The rule does not fix.
Reimplements ESLint's no-nonoctal-decimal-escape.
Options
This rule has no options.
Examples
❌ Incorrect
const label = <input pattern="\8" />
✅ Correct
const digit = '8'
const escaped = '\\8'
Limitations
The original offers suggestions that rewrite the escape as the digit or as a \u escape; this rule reports without them.
no-octal
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid legacy octal number literals such as 071.
Rule Details
A number written with a leading zero, such as 071, is read as octal in sloppy-mode code, so it stands for 57 while most readers see seventy-one, and 08 silently falls back to decimal. In strict code, which includes every module and class body, such literals are a syntax error. Write 0o71 for an octal value or drop the leading zero for a decimal one.
This linter's parser refuses legacy octal literals in every file, scripts included, so code that holds one fails to parse and is reported as a parsing error before any rule runs. The rule therefore never reports by itself; it exists so that a configuration ported from ESLint keeps the same rule id and the same intent.
Reimplements ESLint's no-octal.
Options
This rule has no options.
Examples
A legacy octal literal such as 0755 fails to parse before any rule runs, so the file is reported as a parsing error rather than by this rule.
✅ Correct
const permissions = 0o755
const count = 71
no-prototype-builtins
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid calling hasOwnProperty, isPrototypeOf and propertyIsEnumerable directly on an object.
Rule Details
These methods come from Object.prototype, so calling them on an arbitrary object trusts that object: it may shadow the method with a property of the same name, as parsed JSON can, or lack it entirely when it was made with Object.create(null). The rule reports a call of one of the three methods as a member of any object, through dot or bracket access, optional chaining included. Call them through Object.prototype, or use Object.hasOwn(object, key). The rule does not fix.
Reimplements ESLint's no-prototype-builtins.
Options
This rule has no options.
Examples
❌ Incorrect
const known = payload.hasOwnProperty('id')
const listed = payload?.['propertyIsEnumerable']('id')
const parent = base.isPrototypeOf(child)
✅ Correct
const known = Object.hasOwn(payload, 'id')
const listed = Object.prototype.propertyIsEnumerable.call(payload, 'id')
const parent = Object.prototype.isPrototypeOf.call(base, child)
Limitations
The original offers suggestions that rewrite the call through Object.prototype; this rule reports without them.
no-regex-spaces
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset CODE_RULES
Forbid runs of two or more literal spaces in regular expressions.
Rule Details
A run of spaces in a pattern is hard to count by eye, so /a b/ is easy to misread as two or four spaces. A counted quantifier says how many there are. The rule reports the first run of two or more spaces in a regular expression literal, or in the string pattern of a RegExp(...) call with literal flags. Spaces inside a character class are left alone, and when a quantifier follows a run, the last space belongs to it and is not counted.
The fix replaces the run with one space and a count, so three spaces become {3}. It is withheld for a RegExp string whose source text spells the pattern differently from its value, for example with escapes, where the offsets would not line up.
Reimplements ESLint's no-regex-spaces.
Options
This rule has no options.
Examples
❌ Incorrect
const gap = /total: \d+/
const built = new RegExp('a b')
✅ Correct
const gap = /total: {3}\d+/
const classed = /[ ]/
const quantified = /a +b/
no-restricted-imports
Reads TypeScript, JavaScriptNo preset: turn it on by id
Forbid importing modules, or names from modules, that the configuration restricts.
Rule Details
Some modules should not be used in some places: a heavy library with a lighter alternative, a package's internals, a server module in browser code. The rule matches the source of every import, re-export (export ... from, export * from) and TypeScript import x = require(...) against the configured patterns and reports what they restrict. A pattern restricts the whole module, or only some of the names imported from it, or every name except an allowed few. With no patterns configured the rule reports nothing. The rule does not fix.
Patterns are gitignore-style and matched case-insensitively against the module source split at /: * matches within one segment, ? one character, ** any number of segments. A pattern with no slash, or one starting with **/, matches at any depth; one with a slash is anchored at the start. A pattern matches a path and everything under it, so a pattern naming lodash also matches lodash/fp. A pattern starting with ! excepts the paths it matches; the last matching pattern of a group wins.
Reimplements ESLint's no-restricted-imports, in its patterns object form.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
patterns | object[] | [] | The restrictions, each an object of the fields below. |
| Field | Type | Meaning |
|---|---|---|
group | string[] | Required. The patterns a module source is matched against. |
importNames | string[] | Only these names are restricted; default and * name the default and namespace imports. |
allowImportNames | string[] | Every name except these is restricted. |
message | string | Text appended to the report, such as the alternative to use. |
Of the original's options only the object form of patterns with these four fields is read. The paths option, string entries in patterns, and the regex, caseSensitive, importNamePattern, allowImportNamePattern and allowTypeImports fields are not supported.
Examples
❌ Incorrect, with a restricted module
import merge from 'lodash/merge'
export * from 'lodash'
❌ Incorrect, with restricted names
import React from 'react'
✅ Correct, with a restricted module
import { merge } from 'lodash-es'
import flow from 'lodash/fp'
✅ Correct, with restricted names
import { useState } from 'react'
no-self-assign
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid assigning a variable or property to itself.
Rule Details
value = value has no effect, and almost always stands where a different name was meant, such as this.value = value written as value = value. The rule reports the value side of an =, &&=, ||= or ??= assignment that names the same thing as its target: the same variable, the same member chain (a.b.c = a.b.c, optional chaining looked through), or a matching position inside array and object destructuring ([a, b] = [a, c] reports a). Destructuring positions after a spread are not compared, since the spread may change them. The rule does not fix.
Reimplements ESLint's no-self-assign.
Options
This rule has no options. The original's props option is not read: properties are always checked, the original's default.
Examples
❌ Incorrect
count = count
config.port = config.port
;[first, second] = [first, third]
✅ Correct
count = next
config.port = other.port
;[first, second] = [second, first]
no-shadow-restricted-names
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid declaring a binding named undefined, NaN, Infinity, arguments or eval.
Rule Details
These names refer to values and functions the language provides. A local binding of the same name hides them, so NaN or undefined in that scope no longer means what every reader expects. The rule reports each declaration of one of these names: variables, function and class names, parameters, catch parameters and imports. A let undefined or var undefined declared without an initialiser and never assigned is allowed, since its value is still undefined. The rule does not fix.
Reimplements ESLint's no-shadow-restricted-names.
Options
This rule has no options. The original's reportGlobalThis option is not read, and a binding named globalThis is never reported.
Examples
❌ Incorrect
const NaN = 0
function check(Infinity: number) {}
const undefined = null
try {
run()
} catch (undefined) {}
✅ Correct
const notANumber = Number.NaN
function check(limit: number) {}
let undefined
no-sparse-arrays
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid holes in array literals, such as [1, , 3].
Rule Details
Two commas with nothing between them leave a hole in the array: the index exists in the length but holds no element, and array methods treat it differently from undefined. A hole is easy to miss and is almost always a typo. The rule reports the comma that ends each hole of an array literal; a single trailing comma, as in [1, 2,], is not a hole. Destructuring patterns, where an empty slot skips a value, are not array literals and are not read. The rule does not fix.
Reimplements ESLint's no-sparse-arrays.
Options
This rule has no options.
Examples
❌ Incorrect
const levels = ['low', , 'high']
const leading = [, 1]
✅ Correct
const levels = ['low', undefined, 'high']
const trailing = [1, 2]
const [, second] = levels
no-unexpected-multiline
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid line breaks that make the next line continue the previous expression by accident.
Rule Details
Without semicolons, a line that starts with (, [ or a template literal does not start a new statement: it continues the one before it as a call, a property access or a tagged template, which is rarely what the line break meant. The rule reports:
- a call whose
(starts a new line after the callee, when the call has arguments; - a computed member access whose
[starts a new line after the object; - a tagged template whose literal starts a new line after the tag;
- a division whose operators would read as a regular expression with flags, as in a line
/foo/g.test(x)after an expression.
Optional calls and accesses (?.( and ?.[) are not reported. Put a semicolon at the start of the continued line, or join the lines when the continuation is meant. The rule does not fix.
Reimplements ESLint's no-unexpected-multiline.
Options
This rule has no options.
Examples
❌ Incorrect
const value = load
(source).trim()
const first = items
[0].name
✅ Correct
const value = load
;(source).trim()
const first = items[0].name
no-unsafe-finally
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid return, throw, break and continue that jump out of a finally block.
Rule Details
A finally block runs after the try and catch have decided how the statement ends. A jump that leaves the finally overrides that decision: a return there replaces the value the try returned and silently discards an exception being thrown. The rule reports a return or throw inside a finally block, and a break or continue whose target lies outside it. Jumps that stay inside the block, such as a break out of a loop or switch written within the finally, or a return in a function declared inside it, are not reported. The rule does not fix.
Reimplements ESLint's no-unsafe-finally.
Options
This rule has no options.
Examples
❌ Incorrect
function load(): string {
try {
return read()
} finally {
return 'fallback'
}
}
✅ Correct
function load(): string {
try {
return read()
} finally {
for (const handle of handles) {
if (handle.closed) break
handle.close()
}
}
}
no-unsafe-optional-chaining
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid using an optional chain where its undefined result would throw.
Rule Details
An optional chain such as user?.profile evaluates to undefined when it short-circuits. Used where a value is required, that undefined throws a TypeError, which defeats the point of the ?.. The rule reports a chain, possibly behind &&, ||, ??, a conditional, a sequence or await, used as:
- the callee of a call or of
new, or the tag of a tagged template, as in(user?.greet)(); - the object of a member access, as in
(user?.profile).name; - the value destructured by a declaration, an assignment or a default;
- the source of an array or argument spread, or of a
for...ofloop; - a superclass, or the right side of
inorinstanceof.
Continuing the chain with another ?. is safe and not reported. The rule does not fix.
Reimplements ESLint's no-unsafe-optional-chaining.
Options
This rule has no options. The original's disallowArithmeticOperators option is not read, so arithmetic on an optional chain, which yields NaN rather than throwing, is not reported.
Examples
❌ Incorrect
const name = (user?.profile).name
const { id } = user?.profile
const all = [...user?.roles]
for (const role of user?.roles) {
}
✅ Correct
const name = user?.profile?.name
const { id } = user?.profile ?? {}
const all = [...(user?.roles ?? [])]
no-unused-labels
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset CODE_RULES
Forbid labels that no break or continue names.
Rule Details
A label exists to be the target of a break or continue. One that nothing targets is noise, and often a sign that the jump it was written for was removed or names a different label. The rule reports every label that no break or continue inside its statement names. The fix removes the label and its colon, leaving the statement. It is withheld when a comment sits between the label and the statement, or when removing the label would leave a string literal in a position where it would become a directive such as "use strict".
Reimplements ESLint's no-unused-labels.
Options
This rule has no options.
Examples
❌ Incorrect
outer: for (const row of rows) {
if (row.empty) break
}
✅ Correct
outer: for (const row of rows) {
for (const cell of row.cells) {
if (cell.done) continue outer
}
}
no-unused-private-class-members
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid #private fields, methods and accessors that the class never reads.
Rule Details
A private member can only be used inside its class, so when the class never reads it, it is dead code: left behind by a refactor, or a sign that a read was written against the wrong name. The rule reports each #name member of a class that nothing inside the class reads. Writing to a field does not count as a read, and neither does an increment used as a statement (this.#count++) or an assignment through destructuring. For a getter or setter every use counts, since using it runs code. Each private name resolves to the nearest enclosing class that declares it. The rule does not fix.
Reimplements ESLint's no-unused-private-class-members.
Options
This rule has no options.
Examples
❌ Incorrect
class Counter {
#count = 0
#reset() {}
increment(): void {
this.#count++
}
}
✅ Correct
class Counter {
#count = 0
increment(): number {
this.#count += 1
return this.#count
}
}
Limitations
Members marked with TypeScript's private modifier are ordinary properties at run time and are not read by this rule.
no-useless-assignment
Reads TypeScript, JavaScriptNo preset: turn it on by id
Forbid assigning a value to a variable that nothing reads before it is overwritten or goes out of scope.
Rule Details
A value that is written and then overwritten, or written just before the function ends, does nothing. It is often a bug: the read that should have used it reads a different variable, or a branch that should have returned the value does not. The rule follows the code paths of each function and reports a write, an initialiser included, whose value no read can see before the next write on every path.
Variables that are never read at all are left to typescript/no-unused-vars. A variable read from a nested function, or exported from the module, is not checked, since such a read may happen at any time. The rule does not fix.
Reimplements ESLint's no-useless-assignment.
Options
This rule has no options.
Examples
❌ Incorrect
function label(count: number): string {
let text = 'none'
text = count > 1 ? 'many' : 'one'
return text
}
function total(items: number[]): number {
let sum = 0
for (const item of items) sum += item
sum = 0
return items.length
}
✅ Correct
function label(count: number): string {
let text = 'none'
if (count > 0) text = count > 1 ? 'many' : 'one'
return text
}
no-useless-backreference
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid backreferences in regular expressions that can only ever match the empty string.
Rule Details
A backreference such as \1 or \k<name> matches what its group captured. When the group cannot have matched yet, or its match has been thrown away, the backreference silently matches the empty string, which is never what it was written for. The rule reports a backreference, in a regular expression literal or the string pattern of a RegExp(...) call, that refers to:
- the group that contains it, as in
/(a\1)/; - a group later in the pattern, as in
/\1(a)/; - a group before it while both sit in a lookbehind, which matches right to left;
- a group in another alternative, as in
/(a)|\1b/; - a group inside a negative lookahead or lookbehind that the backreference sits outside of.
The message names the group and the reason. The rule does not fix.
Reimplements ESLint's no-useless-backreference.
Options
This rule has no options.
Examples
❌ Incorrect
const nested = /(a\1)/
const forward = /\1(a)/
const alternative = /(a)|\1b/
const negated = /(?!(a))\1/
✅ Correct
const repeated = /(a)\1/
const named = /(?<quote>['"]).*\k<quote>/
const behind = /(?<=\1(a))b/
no-useless-catch
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid catch clauses that only rethrow the error they caught.
Rule Details
A catch (error) { throw error } changes nothing: the error propagates exactly as it would without the clause. The rule reports a catch clause whose first statement rethrows its own parameter. Without a finally, the whole try statement is reported, since the try itself is then noise; with one, only the catch clause is reported, since the finally still justifies the try. A clause that does anything before rethrowing, such as logging or wrapping the error, is not reported. The rule does not fix.
Reimplements ESLint's no-useless-catch.
Options
This rule has no options.
Examples
❌ Incorrect
try {
run()
} catch (error) {
throw error
}
try {
run()
} catch (error) {
throw error
} finally {
cleanUp()
}
✅ Correct
try {
run()
} catch (error) {
log(error)
throw error
}
try {
run()
} finally {
cleanUp()
}
no-useless-escape
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid backslashes before characters that need no escaping.
Rule Details
An unnecessary backslash is noise, and it makes the escapes that do matter harder to spot. The rule reports each such backslash:
- in a regular expression literal, before a character the pattern grammar reads the same way bare, with the rules of character classes and of the
vflag applied, so/\-/outside a class and/[\.]/are reported while/\./and/[a\-z]/are not; - in a string literal, before a character that is not an escape and not the string's own quote;
- in a template literal, and in a TypeScript template literal type, before a character that is not an escape, a backtick, or the
$that would open a substitution.
Tagged templates are skipped, since the tag sees the raw text, and so are JSX attribute strings. The rule does not fix.
Reimplements ESLint's no-useless-escape.
Options
This rule has no options. The original's allowRegexCharacters option is not read.
Examples
❌ Incorrect
const quote = "it\'s"
const dash = /\-/
const dot = /[\.]/
const template = `\d`
✅ Correct
const quote = "it's"
const escaped = 'it\'s'
const dot = /\./
const range = /[a\-z]/
const template = `\${literal}`
Limitations
The original offers suggestions that remove the backslash or double it; this rule reports without them. Patterns passed to RegExp(...) as strings are read only as strings.
no-var
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset CODE_RULES
Require let or const instead of var.
Rule Details
A var is scoped to its whole function rather than to its block, is hoisted above its declaration, and may be redeclared, so it leaks out of loops and conditionals in ways let and const do not. The rule reports every var declaration, except one inside a declare global block, where TypeScript only accepts var.
The fix turns var into let only where the change keeps the program's meaning: the variable is declared once, used only inside its block and after its declaration, does not become a property of the global object at the top level of a script, and is not a loop variable a closure captures, where let would create a binding per iteration. Choosing const is left to prefer-const.
Reimplements ESLint's no-var.
Options
This rule has no options.
Examples
❌ Incorrect
var count = 0
count += 1
function find(items: string[]): number {
for (var index = 0; index < items.length; index++) {
if (items[index] === 'x') break
}
return index
}
✅ Correct
let count = 0
count += 1
declare global {
var registry: string[]
}
prefer-const
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset CODE_RULES
Require const for let variables that are never reassigned.
Rule Details
A let that is assigned once and never again is a constant in all but name. Declaring it const tells the reader the binding will not change and makes an accidental reassignment an error. The rule reports each let variable written exactly once, by its initialiser or by a single later assignment in the same scope, and read after that. A destructuring is judged variable by variable, so let { a, b } = options reports a alone when only b is reassigned. The counter declared in the head of a classic for loop is never reported; the variable of a for...of or for...in loop is.
The fix rewrites let as const. It is offered only when every variable of the declaration qualifies and the declaration initialises each of them, or heads a for...of or for...in loop; a variable declared without a value and assigned later is reported without a fix.
Reimplements ESLint's prefer-const.
Options
This rule has no options. The original's options are not read: destructuring is fixed to "any" and ignoreReadBeforeAssign to false, the original's defaults.
Examples
❌ Incorrect
let total = compute()
print(total)
for (let item of items) {
print(item)
}
let { port, host } = options
host = 'localhost'
print(port, host)
✅ Correct
const total = compute()
print(total)
let count = 0
count += 1
for (let index = 0; index < 3; print(index)) {}
prefer-rest-params
Reads TypeScript, JavaScriptPreset CODE_RULES
Require rest parameters instead of the arguments object.
Rule Details
arguments is an array-like object, not an array, so it lacks the array methods, it hides which parameters a function takes, and arrow functions do not have one of their own. A rest parameter, (...items), is a real array and shows in the signature. The rule reports each use of the implicit arguments inside a function declaration or expression. Reading a property by name, such as arguments.length, is allowed, and so is a function that declares its own binding called arguments. The rule does not fix.
Reimplements ESLint's prefer-rest-params.
Options
This rule has no options.
Examples
❌ Incorrect
function sum() {
return Array.from(arguments).reduce((total, value) => total + value, 0)
}
function first() {
return arguments[0]
}
✅ Correct
function sum(...values: number[]) {
return values.reduce((total, value) => total + value, 0)
}
function count() {
return arguments.length
}
prefer-spread
Reads TypeScript, JavaScriptPreset CODE_RULES
Require spread syntax instead of .apply() to call a function with an array of arguments.
Rule Details
fn.apply(null, args) was the way to call a function with an array of arguments before spread syntax existed. fn(...args) says the same thing directly and does not make the reader check the this argument. The rule reports a two-argument .apply(...) call that spread can replace exactly: one whose this argument is null, undefined or void 0 for a plain function, or the same object the method is read from, as in math.max.apply(math, values). Calls whose argument list is an array literal, and calls that pass a different this, are left alone. The rule does not fix.
Reimplements ESLint's prefer-spread.
Options
This rule has no options.
Examples
❌ Incorrect
const largest = max.apply(null, values)
const joined = parts.push.apply(parts, extra)
✅ Correct
const largest = max(...values)
parts.push(...extra)
const bound = handler.apply(context, values)
require-yield
Reads TypeScript, JavaScriptPreset CODE_RULES
Require generator functions with a body to contain yield.
Rule Details
A generator that never yields produces an iterator that finishes at once, with its return value as the only result. That is rarely the intent: either a yield is missing, or the function was meant to be an ordinary one. The rule reports a generator function declaration or expression, generator methods included, whose body has statements but no yield. An empty generator, function* () {}, is allowed, since it is a deliberate empty iterator. The rule does not fix.
Reimplements ESLint's require-yield.
Options
This rule has no options.
Examples
❌ Incorrect
function* ids() {
return [1, 2, 3]
}
✅ Correct
function* ids() {
yield 1
yield 2
}
function* none() {}
use-isnan
Reads TypeScript, JavaScriptPreset CODE_RULES
Require Number.isNaN() instead of comparing with NaN.
Rule Details
NaN is the one value that is not equal to itself, so value === NaN is always false and value !== NaN always true, whatever the value. The rule reports a comparison (==, ===, !=, !==, <, >, <=, >=) with NaN or Number.NaN on either side. By default it also reports switch (NaN) and case NaN:, which can never match for the same reason. Use Number.isNaN(value). The rule does not fix.
Reimplements ESLint's use-isnan.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
enforceForSwitchCase | boolean | true | Whether switch (NaN) and case NaN: are reported. |
The original's enforceForIndexOf option is not read, so indexOf(NaN) and lastIndexOf(NaN) are not reported.
Examples
❌ Incorrect
if (value === NaN) {
reset()
}
switch (value) {
case NaN:
reset()
}
✅ Correct
if (Number.isNaN(value)) {
reset()
}
✅ Correct, with { "enforceForSwitchCase": false }
switch (value) {
case NaN:
reset()
}
Limitations
The original offers suggestions that rewrite a comparison as a Number.isNaN() call; this rule reports without them.
valid-typeof
Reads TypeScript, JavaScriptPreset CODE_RULES
Require typeof to be compared with one of the strings it can produce.
Rule Details
typeof yields one of eight strings: "undefined", "object", "boolean", "number", "bigint", "string", "symbol" and "function". A comparison with anything else, usually a misspelling such as "strnig" or a guess such as "array", can never hold. The rule reports the other side of an ==, ===, != or !== comparison with a typeof expression when it is a literal, or a template without substitutions, that is not one of those strings, and when it is the global undefined rather than the string "undefined". The rule does not fix.
Reimplements ESLint's valid-typeof.
Options
This rule has no options. The original's requireStringLiterals option is not read, so a comparison with a variable or another expression is never reported.
Examples
❌ Incorrect
const isText = typeof value === 'strnig'
const isList = typeof value === 'array'
const isMissing = typeof value === undefined
✅ Correct
const isText = typeof value === 'string'
const isList = Array.isArray(value)
const isMissing = typeof value === 'undefined'
const isSame = typeof value === typeof other
Limitations
The original offers a suggestion that turns a comparison with undefined into one with the string; this rule reports without it.
TypeScript rules
Reimplemented from typescript-eslint, without type information. CODE_RULES turns on the ones its recommended configuration does.
typescript/adjacent-overload-signatures
Reads TypeScript, JavaScriptPreset CODE_RULES
Require the overload signatures of a function, method or constructor to sit next to each other.
Rule Details
Overloads read as one unit only when they are declared together. This rule reports a signature that repeats a name already seen in the same body after some other member came in between. It checks top-level statements, blocks, namespaces, classes, interfaces and type literals, and keeps static and instance members, quoted and bare names, and private names apart. It does not fix.
Reimplements typescript-eslint's adjacent-overload-signatures.
Options
This rule has no options.
Examples
❌ Incorrect
interface Store {
get(key: string): string
set(key: string, value: string): void
get(key: number): string
}
✅ Correct
interface Store {
get(key: string): string
get(key: number): string
set(key: string, value: string): void
}
typescript/ban-ts-comment
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid @ts-ignore and @ts-nocheck, and require a description on @ts-expect-error.
Rule Details
A @ts- directive changes what the compiler reports, so each one should be deliberate and explained. This rule reports @ts-ignore in favour of @ts-expect-error, which fails once the line it covers stops erroring. It reports a @ts-expect-error whose description is shorter than three characters, counted as graphemes. It reports @ts-nocheck only where the compiler honours it, before the first statement of the file, and leaves @ts-check alone. It does not fix.
Reimplements typescript-eslint's ban-ts-comment.
Options
This rule has no options. The original's options, which allow or require a description per directive, set a description format and change the minimum length, are not read: the behaviour is fixed to the original's defaults.
Examples
❌ Incorrect
// @ts-ignore
const port: number = '8080'
// @ts-expect-error
const port: number = '8080'
✅ Correct
// @ts-expect-error: the fixture checks the runtime guard
const port: number = '8080'
Limitations
The original offers a suggestion that rewrites @ts-ignore to @ts-expect-error; this rule reports without one.
typescript/consistent-type-assertions
Reads TypeScript, JavaScript🔧 Fixable with --fixNo preset: turn it on by id
Require type assertions to be written value as Type rather than <Type>value.
Rule Details
The angle-bracket form reads like JSX and cannot be written in a .tsx file at all, so one assertion style keeps code portable between the two. This rule reports every angle-bracket assertion. The fix rewrites it to the as form and keeps the meaning: it parenthesises the value when the value binds no tighter than as, and parenthesises the whole assertion when the surrounding expression binds at least as tight, unless it already sits in parentheses of its own.
Reimplements typescript-eslint's consistent-type-assertions.
Options
This rule has no options. The original's options, which choose the angle-bracket style or forbid assertions, and restrict assertions on object and array literals, are not read: the style is fixed to as and literals are not checked.
Examples
❌ Incorrect
const input = <HTMLInputElement>event.target
const total = <number>(a ?? b) + 1
✅ Correct
const input = event.target as HTMLInputElement
const total = ((a ?? b) as number) + 1
typescript/consistent-type-imports
Reads TypeScript, JavaScript🔧 Fixable with --fixNo preset: turn it on by id
Require import type for imports used only as types, and forbid import() type annotations.
Rule Details
A type-only import is erased by the compiler, so marking it keeps the emitted module free of imports it never needs. This rule reports a value import declaration whose bindings are used only in type positions, including typeof queries and computed keys of property signatures. A re-exported binding counts as a value use. A declaration with import attributes is left alone. The fix adds type when every binding is type-only, and otherwise moves the type-only bindings to a separate import type of the same module, joining one that already exists. The rule also reports import('...') used as a type annotation, without a fix.
Reimplements typescript-eslint's consistent-type-imports.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
disallowTypeAnnotations | boolean | true | Report import() type annotations. |
prefer | 'type-imports' | 'type-imports' | Accepted for compatibility; the only value. |
fixStyle | 'separate-type-imports' | 'separate-type-imports' | Accepted for compatibility; the only value. |
The original's 'no-type-imports' preference and 'inline-type-imports' fix style are not supported.
Examples
❌ Incorrect
import { Config, load } from './config'
export const read = (path: string): Config => load(path)
let config: import('./config').Config
✅ Correct
import type { Config } from './config'
import { load } from './config'
export const read = (path: string): Config => load(path)
Limitations
The original keeps value imports that decorator metadata needs when emitDecoratorMetadata and experimentalDecorators are on. This rule does not read compiler options, so in that setup a class used only in decorated signatures is still reported.
typescript/no-array-constructor
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset CODE_RULES
Forbid building arrays with the Array constructor where a literal says the same.
Rule Details
Array(a, b) and new Array() produce what [a, b] and [] spell out directly, and the constructor's behaviour changes with the number of arguments. This rule reports calls and constructions of Array with zero or several arguments. A single argument sets a length, so that form is allowed, and so is a call with type arguments such as new Array<string>(), which a literal cannot express. The fix replaces the call with a literal holding the argument text, comments included.
Reimplements typescript-eslint's no-array-constructor.
Options
This rule has no options.
Examples
❌ Incorrect
const empty = new Array()
const pair = Array(1, 2)
✅ Correct
const empty = []
const pair = [1, 2]
const slots = new Array(3)
const names = new Array<string>()
Limitations
The rule matches the name Array and does not check whether a local binding shadows the global.
typescript/no-duplicate-enum-values
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid two members of one enum from having the same literal value.
Rule Details
An enum member that repeats another's value is almost always a copied line that was not edited, and it makes the two names indistinguishable at run time. This rule reports every member whose initializer repeats a value seen earlier in the same enum. Values are compared when they are string or number literals, templates without substitutions, or numbers under a unary plus or minus. Strings and numbers never collide with each other, and -0 is distinct from 0. Members without initializers and computed values are not compared. It does not fix.
Reimplements typescript-eslint's no-duplicate-enum-values.
Options
This rule has no options.
Examples
❌ Incorrect
enum Status {
Active = 'active',
Paused = 'active',
}
✅ Correct
enum Status {
Active = 'active',
Paused = 'paused',
}
typescript/no-empty-function
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid functions whose body is empty.
Rule Details
An empty function body is either unfinished work or a deliberate no-op, and a reader cannot tell which. This rule reports a function declaration, function expression, arrow function, method, getter, setter or constructor whose block body holds neither a statement nor a comment. A comment inside the body, such as one saying why nothing happens, is enough to pass. A constructor that declares parameter properties passes too, since its parameter list does the work. It does not fix.
Reimplements typescript-eslint's no-empty-function.
Options
This rule has no options. The original's options, a list of function kinds to allow, are not read: every kind is checked.
Examples
❌ Incorrect
function noop() {}
class Store {
reset() {}
}
✅ Correct
function noop() {
// intentionally empty: callers pass it where a handler is required
}
class Store {
constructor(private readonly name: string) {}
}
typescript/no-empty-interface
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset CODE_RULES
Forbid interfaces that declare no members.
Rule Details
An interface without members is the same as {}, which accepts any value except null and undefined, and an empty interface that extends one type is just that type under another name. This rule reports both. An empty interface extending two or more types passes, since it names their combination. The fix rewrites the single-supertype case as a type alias, keeping the type parameters; it is withheld when the interface merges with a class of the same name, or is declared inside an ambient module of a declaration file, where an alias would change meaning. The memberless case without a supertype has no fix.
Reimplements typescript-eslint's no-empty-interface, which typescript-eslint has since folded into no-empty-object-type.
Options
This rule has no options. The original's allowSingleExtends option is not read: an empty interface extending one type is always reported.
Examples
❌ Incorrect
interface Props {}
interface ButtonProps extends Props {}
✅ Correct
type ButtonProps = Props
interface LinkProps extends Props, Anchor {}
interface Props {
label: string
}
Limitations
Where the fix is withheld, the original offers the type alias as a suggestion; this rule reports without one.
typescript/no-empty-object-type
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid the empty object type, written {} or as an interface without members.
Rule Details
The type {} accepts every value except null and undefined, literals such as 0 and "" included, which is rarely what its author meant: object says "any object" and unknown says "any value". This rule reports a {} type literal and an interface that declares no members and extends nothing. An interface that declares no members and extends exactly one type is reported as equivalent to that type; one extending two or more passes. A {} inside an intersection passes, since T & {} is the idiom for a non-nullable T. It does not fix.
Reimplements typescript-eslint's no-empty-object-type.
Options
This rule has no options. The original's options, which allow empty interfaces, allow {} literals or exempt names matching a pattern, are not read, although the report text still names the option that would allow the construct upstream.
Examples
❌ Incorrect
let value: {}
interface Props {}
interface ButtonProps extends Props {}
✅ Correct
let value: object
let anything: unknown
type NonNullable<T> = T & {}
interface LinkProps extends Props, Anchor {}
Limitations
The original offers suggestions that replace {} with object or unknown, and that turn a single-supertype interface into a type alias; this rule reports without them.
typescript/no-explicit-any
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid writing the any type.
Rule Details
The any type switches type checking off for every value it touches, and the loss spreads through whatever the value flows into. This rule reports every any written in a type position, so that a precise type is chosen, or unknown where the type really is not known and must be narrowed before use. It does not fix.
Reimplements typescript-eslint's no-explicit-any.
Options
This rule has no options. The original's options, which offer an unknown fix and exempt any[] in rest parameters, are not read.
Examples
❌ Incorrect
function parse(text: string): any {
return JSON.parse(text)
}
✅ Correct
function parse(text: string): unknown {
return JSON.parse(text)
}
Limitations
The original offers suggestions that replace any with unknown or never; this rule reports without them.
typescript/no-extra-non-null-assertion
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset CODE_RULES
Forbid non-null assertions that have no effect.
Rule Details
A second ! on an already asserted value changes nothing, and a ! directly before an optional chain contradicts it: the assertion says the value is never nullish while ?. says it may be. This rule reports a non-null assertion that is asserted again, or that is the object of an optional member access or the callee of an optional call. The fix removes the extra !.
Reimplements typescript-eslint's no-extra-non-null-assertion.
Options
This rule has no options.
Examples
❌ Incorrect
const name = user!!.name
const size = list!?.length
const result = handler!?.()
✅ Correct
const name = user!.name
const size = list?.length
const result = handler?.()
typescript/no-inferrable-types
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset CODE_RULES
Forbid type annotations that repeat the type of a literal initializer.
Rule Details
const count: number = 0 says twice what const count = 0 says once. This rule reports an annotation of bigint, boolean, null, number, string, symbol, undefined or RegExp on a variable, a parameter with a default, or a class property, when the initializer is plainly a value of that type: a literal, a template, a negated or signed number, !x, void x, NaN, Infinity, or a call of the matching global such as String(x) or new RegExp(x). Readonly and optional properties are left alone, since there the annotation widens a literal type. The fix removes the annotation, together with the ? of an optional parameter or the ! of a definite property that would be left dangling.
Reimplements typescript-eslint's no-inferrable-types.
Options
This rule has no options. The original's options, which exempt parameters or properties, are not read: both are always checked.
Examples
❌ Incorrect
const retries: number = 3
const label: string = `retry ${retries}`
function wait(ms: number = 100) {
return ms
}
✅ Correct
const retries = 3
const label = `retry ${retries}`
function wait(ms = 100) {
return ms
}
typescript/no-misused-new
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid new in classes and constructor in interfaces, where the two have been confused.
Rule Details
A class declares its constructor with constructor, and an interface describes construction with a new signature. This rule reports an interface construct signature that returns the interface itself, an interface member named constructor, and a bodiless class method named new that returns the class. Each of these compiles but does not describe what its author meant. It does not fix.
Reimplements typescript-eslint's no-misused-new.
Options
This rule has no options.
Examples
❌ Incorrect
interface Point {
new (x: number, y: number): Point
constructor(): void
}
declare class Vector {
new(): Vector
}
✅ Correct
interface PointConstructor {
new (x: number, y: number): Point
}
declare class Vector {
constructor()
}
typescript/no-namespace
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid TypeScript namespaces and module Name blocks outside declaration files.
Rule Details
ES modules already give every file its own scope and an explicit import and export surface, so a namespace adds a second, older way to group code. This rule reports every namespace Name {} and module Name {} declaration in a source file. Declaration files (.d.ts) are exempt, since ambient namespaces there describe globals that already exist. A declare global {} augmentation and an ambient module named by a string, such as declare module 'pkg' {}, pass everywhere. It does not fix.
Reimplements typescript-eslint's no-namespace.
Options
This rule has no options. The original's options, which allow declare namespaces in source files or turn off the declaration-file exemption, are not read: the behaviour is fixed to the original's defaults.
Examples
❌ Incorrect
namespace Geometry {
export const origin = { x: 0, y: 0 }
}
✅ Correct
export const origin = { x: 0, y: 0 }
declare module 'legacy-lib' {
export const version: string
}
declare global {
interface Window {
analytics: unknown
}
}
typescript/no-non-null-asserted-optional-chain
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid a non-null assertion on the result of an optional chain.
Rule Details
An optional chain evaluates to undefined when a link is missing, by design. A ! after the chain, or after the chain in parentheses, tells the compiler that cannot happen, which hides exactly the case the chain was written to handle. This rule reports both shapes. It does not fix: drop the ! and handle undefined, or drop the ?. if the value is known to exist.
Reimplements typescript-eslint's no-non-null-asserted-optional-chain.
Options
This rule has no options.
Examples
❌ Incorrect
const city = user?.address!
const zip = (user?.address)!.zip
✅ Correct
const city = user?.address
const zip = user!.address.zip
Limitations
The original offers a suggestion that removes the !; this rule reports without one.
typescript/no-non-null-assertion
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid the non-null assertion operator !.
Rule Details
A postfix ! tells the compiler a value is never null or undefined without checking, so a wrong assumption surfaces as a runtime error far from its cause. This rule reports every non-null assertion. It does not fix: narrow the value with a check, use optional chaining, or give it a type that cannot be nullish.
Reimplements typescript-eslint's no-non-null-assertion.
Options
This rule has no options.
Examples
❌ Incorrect
const root = document.getElementById('root')!
root.append('ready')
✅ Correct
const root = document.getElementById('root')
if (root !== null) root.append('ready')
Limitations
The original offers suggestions that replace x!.y with x?.y; this rule reports without them.
typescript/no-this-alias
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid copying this into a variable.
Rule Details
Storing this in a variable such as self or that is a habit from before arrow functions, which keep the surrounding this without a copy. This rule reports a variable declaration or an assignment whose target is a plain identifier and whose value is this. Destructuring members out of this passes. It does not fix.
Reimplements typescript-eslint's no-this-alias.
Options
This rule has no options. The original's options, which forbid destructuring and allow chosen alias names, are not read: destructuring always passes and no name is allowed.
Examples
❌ Incorrect
class Timer {
start() {
const self = this
setTimeout(function () {
self.tick()
}, 1000)
}
}
✅ Correct
class Timer {
start() {
const { tick } = this
setTimeout(() => this.tick(), 1000)
}
}
typescript/no-unnecessary-type-constraint
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid constraining a type parameter to any or unknown.
Rule Details
A type parameter without a constraint already accepts every type, so extends any and extends unknown add nothing but noise. This rule reports every type parameter declared with one of those two constraints, on functions, classes, interfaces, type aliases and methods alike. It does not fix: delete the constraint.
Reimplements typescript-eslint's no-unnecessary-type-constraint.
Options
This rule has no options.
Examples
❌ Incorrect
function first<T extends unknown>(items: T[]): T | undefined {
return items[0]
}
interface Box<T extends any> {
value: T
}
✅ Correct
function first<T>(items: T[]): T | undefined {
return items[0]
}
interface Box<T extends object> {
value: T
}
Limitations
The original offers a suggestion that removes the constraint, keeping <T,> in a .tsx arrow function; this rule reports without one.
typescript/no-unsafe-declaration-merging
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid a class and an interface of the same name in one scope.
Rule Details
When a class and an interface share a name, TypeScript merges them: the interface's members join the class's type, but the class never has to implement them, so the compiler accepts calls that fail at run time. This rule reports both declarations of such a pair. A class whose name is merged only with a namespace, or an interface merged only with another interface, passes. It does not fix.
Reimplements typescript-eslint's no-unsafe-declaration-merging.
Options
This rule has no options.
Examples
❌ Incorrect
interface Logger {
flush(): void
}
class Logger {
log(message: string) {
return message
}
}
✅ Correct
interface Flushable {
flush(): void
}
class Logger implements Flushable {
flush() {
return undefined
}
}
typescript/no-unsafe-function-type
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid the global Function type.
Rule Details
The built-in Function type accepts any callable, including classes, and types every call through it as returning any, so neither the arguments nor the result are checked. This rule reports Function used as a type, and in the extends of an interface or the implements of a class, when it refers to the global. A type named Function that the file declares or imports itself passes. It does not fix: write a function type with its parameters and return type.
Reimplements typescript-eslint's no-unsafe-function-type.
Options
This rule has no options.
Examples
❌ Incorrect
function retry(task: Function, times: number) {
return task
}
✅ Correct
function retry(task: () => Promise<void>, times: number) {
return task
}
typescript/no-unused-expressions
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid expression statements whose value is thrown away.
Rule Details
A statement that neither calls, assigns, awaits, yields, deletes nor uses void does nothing with the value it computes, which is usually a typo or a half-finished edit. This rule reports a statement made of a literal, a name, a member access, a binary, logical, conditional or unary operator, a sequence, an array, object, function or class expression, a template or a tagged template. Type assertions, non-null assertions, instantiation expressions and optional chains are looked through to the expression underneath. Directive prologue strings such as 'use strict', dynamic imports and JSX elements pass. It does not fix.
Reimplements typescript-eslint's no-unused-expressions.
Options
This rule has no options. The original's options, which allow short-circuit and ternary expressions, allow tagged templates and check JSX, are not read: the behaviour is fixed to the original's defaults.
Examples
❌ Incorrect
config.enabled
ready && start()
count > 0 ? show() : hide()
user!.name as string
✅ Correct
'use strict'
start()
config.enabled = true
if (ready) start()
void import('./plugin')
typescript/no-unused-vars
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid variables, parameters, imports, types and namespaces that are declared and never used.
Rule Details
A declaration nothing reads is dead code or a sign of a mistake, such as a misspelt name or a forgotten call. This rule reports it at the declaration, or at its last write in its own scope when it is only ever assigned. A read that serves only the variable's own update, such as count += 1 as a statement, does not count as a use, and neither does a function or type referring to itself. A value read only inside typeof queries is reported as used only as a type, unless it is an import. Exported declarations count as used, as do the declarations of a declaration file or a declare namespace that has no explicit export list. It does not fix.
Parameters before the last used one, setter parameters, parameters of signatures and function types, constructor parameter properties, enum members and mapped type keys are never reported. Unused catch bindings and properties destructured beside a rest element are reported.
Reimplements typescript-eslint's no-unused-vars.
Options
This rule has no options. The original's options, among them the ignore patterns for names such as a leading underscore, the parameter and caught-error modes, and the rest-sibling exemption, are not read: the behaviour is fixed to the original's defaults, with no ignore pattern.
Examples
❌ Incorrect
import { readFileSync } from 'node:fs'
let attempts = 0
attempts += 1
export function greet(name: string, _unused: number) {
const greeting = `Hello, ${name}`
}
✅ Correct
import { readFileSync } from 'node:fs'
export function load(_path: string, encoding: BufferEncoding) {
return readFileSync('config.json', encoding)
}
typescript/no-wrapper-object-types
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset CODE_RULES
Forbid the wrapper object types String, Number, Boolean, BigInt, Symbol and Object as types.
Rule Details
The upper-cased wrapper types describe boxed objects, not the primitives almost every value is: a string is not assignable from a String, and Object admits every value except null and undefined. This rule reports each wrapper used as a type, or in the extends of an interface or the implements of a class, when the name refers to the global. A type of the same name that the file declares or imports itself passes. The fix replaces a type reference with the lower-cased primitive (Object becomes object); a heritage clause is reported without a fix.
Reimplements typescript-eslint's no-wrapper-object-types.
Options
This rule has no options.
Examples
❌ Incorrect
function shout(text: String): Number {
return text.length
}
✅ Correct
function shout(text: string): number {
return text.length
}
typescript/prefer-as-const
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset CODE_RULES
Require as const over asserting or annotating a literal with its own literal type.
Rule Details
'get' as 'get' and let method: 'get' = 'get' write the same literal twice, and the two copies can drift apart in an edit. as const says it once. This rule reports a string, number or boolean literal asserted or annotated with a literal type spelt exactly the same, on variables and class properties. The fix rewrites an assertion to as const; an annotation is reported without a fix, because removing it changes the declared type.
Reimplements typescript-eslint's prefer-as-const.
Options
This rule has no options.
Examples
❌ Incorrect
const request = { method: 'get' as 'get' }
let mode: 'strict' = 'strict'
✅ Correct
const request = { method: 'get' as const }
let mode = 'strict' as const
let level: 'low' | 'high' = 'low'
Limitations
The literal and its type are compared as written, so 'get' as "get" is not reported. The original offers a suggestion for the annotation case; this rule reports without one.
typescript/prefer-namespace-keyword
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset CODE_RULES
Require namespace rather than module to declare a TypeScript namespace.
Rule Details
module Name {} is the old spelling of a namespace, and it reads as though it declared an ES module. This rule reports a namespace declared with the module keyword and an identifier name, dotted names included. An ambient module named by a string, such as declare module 'pkg' {}, is a different construct and passes. The fix replaces the keyword with namespace.
Reimplements typescript-eslint's prefer-namespace-keyword.
Options
This rule has no options.
Examples
❌ Incorrect
declare module Shapes {
const sides: number
}
✅ Correct
declare namespace Shapes {
const sides: number
}
declare module 'shapes' {
export const sides: number
}
typescript/triple-slash-reference
Reads TypeScript, JavaScriptPreset CODE_RULES
Forbid /// <reference path> directives, and /// <reference types> directives for a module the file also imports.
Rule Details
A triple-slash reference pulls a file or a types package into the program from a comment, where an import would state the dependency in code. This rule reads the reference directives before the first statement. It reports every path= reference, and a types= reference whenever the same module is also imported with an import declaration or import x = require(), once per such import. A lib= reference passes, since nothing can import a built-in library declaration. It does not fix.
Reimplements typescript-eslint's triple-slash-reference.
Options
This rule has no options. The original's options, which set the policy for each kind of reference, are not read: the behaviour is fixed to the original's defaults.
Examples
❌ Incorrect
/// <reference path="./globals.d.ts" />
/// <reference types="node" />
import { readFileSync } from 'node'
✅ Correct
/// <reference lib="dom" />
/// <reference types="vite/client" />
import { readFileSync } from 'node:fs'
JSDoc rules
Reimplemented from eslint-plugin-jsdoc. JSDOC_RULES turns on the ones its recommended configuration does.
| Rule | Reads | 🔧 | JSDOC_RULES |
|---|---|---|---|
jsdoc/check-access | TypeScript, JavaScript | ✅ | |
jsdoc/check-param-names | TypeScript, JavaScript | ✅ | |
jsdoc/check-property-names | TypeScript, JavaScript | ✅ | |
jsdoc/check-tag-names | TypeScript, JavaScript | 🔧 | ✅ |
jsdoc/check-types | TypeScript, JavaScript | 🔧 | ✅ |
jsdoc/check-values | TypeScript, JavaScript | ✅ | |
jsdoc/empty-tags | TypeScript, JavaScript | 🔧 | ✅ |
jsdoc/escape-inline-tags | TypeScript, JavaScript | ✅ | |
jsdoc/implements-on-classes | TypeScript, JavaScript | ✅ | |
jsdoc/informative-docs | TypeScript, JavaScript | ||
jsdoc/no-defaults | TypeScript, JavaScript | 🔧 | ✅ |
jsdoc/no-types | TypeScript, JavaScript | 🔧 | ✅ |
jsdoc/reject-any-type | TypeScript, JavaScript | ✅ | |
jsdoc/reject-function-type | TypeScript, JavaScript | ✅ | |
jsdoc/require-description | TypeScript, JavaScript | ||
jsdoc/require-jsdoc | TypeScript, JavaScript | 🔧 | ✅ |
jsdoc/require-next-type | TypeScript, JavaScript | ✅ | |
jsdoc/require-param | TypeScript, JavaScript | 🔧 | ✅ |
jsdoc/require-param-description | TypeScript, JavaScript | ✅ | |
jsdoc/require-param-name | TypeScript, JavaScript | ✅ | |
jsdoc/require-property | TypeScript, JavaScript | 🔧 | ✅ |
jsdoc/require-property-description | TypeScript, JavaScript | ✅ | |
jsdoc/require-property-name | TypeScript, JavaScript | ✅ | |
jsdoc/require-returns | TypeScript, JavaScript | ✅ | |
jsdoc/require-returns-check | TypeScript, JavaScript | ✅ | |
jsdoc/require-returns-description | TypeScript, JavaScript | ✅ | |
jsdoc/require-throws-type | TypeScript, JavaScript | ✅ | |
jsdoc/require-yields | TypeScript, JavaScript | ✅ | |
jsdoc/require-yields-check | TypeScript, JavaScript | ✅ | |
jsdoc/require-yields-type | TypeScript, JavaScript | ✅ | |
jsdoc/ts-no-empty-object-type | TypeScript, JavaScript | ✅ | |
jsdoc/valid-types | TypeScript, JavaScript | ✅ |
jsdoc/check-access
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require a JSDoc block's access control to name a valid level, once.
Rule Details
Access control in JSDoc is written either as @access <level> or as one of the shorthand tags @package, @private, @protected and @public. This rule reports an @access tag whose level is not one of package, private, protected or public, a block that combines @access with a shorthand tag, and a block carrying more than one access-control tag. Every JSDoc block in the file is read, whether or not it documents a declaration. It does not fix.
Reimplements eslint-plugin-jsdoc's check-access.
Options
This rule has no options.
Examples
❌ Incorrect
/**
* Clears the cache.
*
* @access internal
*/
export function clear(): void {
return undefined
}
/**
* Resets the store.
*
* @access private
* @public
*/
export function reset(): void {
return undefined
}
✅ Correct
/**
* Clears the cache.
*
* @access package
*/
export function clear(): void {
return undefined
}
jsdoc/check-param-names
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require the @param tags of a function's JSDoc to name its parameters, in order.
Rule Details
A @param tag that names the wrong parameter documents something the function does not have, and it is the first thing to go stale after a rename. On functions, methods and method signatures, this rule reports a @param name that differs from the parameter at its position, a tag past the last parameter, and a duplicate tag. For a destructured parameter documented with no type or a plain object type, it also reports a property left out of the dotted @param root.property tags, a documented property the pattern does not have, and a dotted path whose root is not documented. A this parameter may be left undocumented. It stops at the first problem in a block and does not fix.
Reimplements eslint-plugin-jsdoc's check-param-names.
Options
This rule has no options. The original's options, which allow extra trailing tags, turn off the destructuring and missing-tag checks, choose which types skip property checks and enable a fixer, are not read: the behaviour is fixed to the original's defaults.
Examples
❌ Incorrect
/**
* Moves a point.
*
* @param x - The horizontal offset
* @param z - The vertical offset
*/
export function move(x: number, y: number): void {
return undefined
}
/**
* Opens a connection.
*
* @param options - The settings
* @param options.host - The host
*/
export function open({ host, port }: Settings): void {
return undefined
}
✅ Correct
/**
* Opens a connection.
*
* @param options - The settings
* @param options.host - The host
* @param options.port - The port
*/
export function open({ host, port }: Settings): void {
return undefined
}
jsdoc/check-property-names
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require each @property of a JSDoc block to be named once, with dotted paths after their root.
Rule Details
A @property tag documents a member of a @typedef or of a documented object. This rule reports a property name that appears twice for the same type definition, a block holding several @typedef tags being checked one definition at a time. When there are no duplicates, it reports a dotted path such as @property options.port whose root property is not documented. Every JSDoc block in the file is read. It does not fix.
Reimplements eslint-plugin-jsdoc's check-property-names.
Options
This rule has no options. The original's enableFixer option, which removes duplicates, is not read.
Examples
❌ Incorrect
/**
* A server address.
*
* @typedef {object} Address
* @property {string} host - The host
* @property {string} host - The host name
*/
/**
* A connection.
*
* @typedef {object} Connection
* @property {number} options.port - The port
*/
✅ Correct
/**
* A connection.
*
* @typedef {object} Connection
* @property {object} options - The settings
* @property {number} options.port - The port
*/
jsdoc/check-tag-names
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset JSDOC_RULES
Require JSDoc block and inline tags to be known tags, written by their preferred names.
Rule Details
A misspelt or invented tag is silently ignored by documentation tools. This rule reports a block tag that JSDoc, TypeScript and TypeDoc do not know and the configuration does not define, and an inline tag other than {@link}, {@linkcode}, {@linkplain}, {@tutorial}, {@inheritDoc}, {@label}, {@include} and {@includeCode}. A synonym, such as @return or @arg, is reported and the fix renames it to the preferred tag, @returns or @param.
With the typed option on, tags that the type system makes redundant are reported too: @type, @typedef, @property, @private and similar tags everywhere, and tags such as @function, @static or @memberof outside ambient declarations (declare and the top level of .d.ts files), as well as an unnamed @template. The fix removes such a tag, or keeps its description and drops the type and, where the tag is always redundant, the tag name.
Reimplements eslint-plugin-jsdoc's check-tag-names.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
typed | boolean | false | Report tags a type system makes redundant. |
definedTags | string[] | [] | Extra block tag names to accept, without @. |
The original's options that turn the fixer off, accept JSX pragma tags and configure the inline tags are not read: fixes are always offered, JSX pragma tags are accepted only when defined, and the inline tag list is fixed.
Examples
❌ Incorrect
/**
* Doubles a number.
*
* @arg value - The number
* @return The double
* @pure
*/
export function double(value: number): number {
return value * 2
}
/**
* A point.
*
* @typedef {object} Point
*/
export interface Point {
x: number
}
✅ Correct
/**
* Doubles a number, see {@link triple}.
*
* @param value - The number
* @returns The double
* @pure
*/
export function double(value: number): number {
return value * 2
}
Limitations
The original chooses its known tags by the mode setting and renames tags by the tag name preference setting. This rule reads neither: it accepts the union of the JSDoc, TypeScript and TypeDoc tags and applies the default preferences.
jsdoc/check-types
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset JSDOC_RULES
Require the types in JSDoc tags to spell native types the way TypeScript does.
Rule Details
A JSDoc type written {String} or {regexp} names a native type in the wrong case, and tools disagree on what it means. This rule reads the type of every typed tag, such as @param, @returns or @type, in every JSDoc block, and reports a name that spells undefined, null, boolean, number, bigint, string, symbol, Array, Function, Date or RegExp in another case, and Object where object is meant. The fix respells the names. It also reports object or Object given type arguments, such as Object.<string, number>, in favour of an index signature, without a fix.
Reimplements eslint-plugin-jsdoc's check-types.
Options
This rule has no options. The original's options, which exempt tag contexts, skip defaults and check parent and child types together, are not read.
Examples
❌ Incorrect
/**
* Counts the words of a text.
*
* @param {String} text - The text
* @returns {Object.<string, Number>} The counts
*/
export function count(text: string): Record<string, number> {
return { [text]: 1 }
}
✅ Correct
/**
* Counts the words of a text.
*
* @param {string} text - The text
* @returns {{[word: string]: number}} The counts
*/
export function count(text: string): Record<string, number> {
return { [text]: 1 }
}
Limitations
The original picks preferred spellings by its mode setting and accepts a preferred types setting. This rule reads neither: the spellings are always those of TypeScript mode.
jsdoc/check-values
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require JSDoc tags that take a value to carry a valid one.
Rule Details
Several JSDoc tags are only useful with a well-formed value. In every JSDoc block, this rule reports a @version or @since that is missing or is not a semantic version, a @kind that is missing or is not one of JSDoc's kinds (class, constant, event, external, file, function, member, mixin, module, namespace, typedef), a @license that is missing or is not a valid SPDX expression, an empty @author or @variation, and an @import whose text is not an import declaration with a quoted module. It does not fix.
Reimplements eslint-plugin-jsdoc's check-values.
Options
This rule has no options. The original's options, which restrict authors and licenses, set a license pattern and require numeric variations, are not read.
Examples
❌ Incorrect
/**
* The cache.
*
* @since next
* @license MIT-ish
* @kind thing
*/
export const cache = new Map<string, string>()
✅ Correct
/**
* The cache.
*
* @since 1.2.0
* @license MIT OR Apache-2.0
* @kind constant
*/
export const cache = new Map<string, string>()
jsdoc/empty-tags
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset JSDOC_RULES
Require JSDoc marker tags, such as @private or @readonly, to carry no text.
Rule Details
Some tags only mark a symbol, and text after them is either ignored or misread as a name or type. In every JSDoc block, this rule reports one of @abstract, @async, @generator, @global, @hideconstructor, @ignore, @inheritDoc, @inner, @instance, @internal, @overload, @override, @package, @private, @protected, @public, @readonly and @static followed by a type, a name or a description. The fix clears the text after the tag on its line.
Reimplements eslint-plugin-jsdoc's empty-tags.
Options
This rule has no options. The original's tags option, which adds tags to the list, is not read.
Examples
❌ Incorrect
/**
* The retry limit.
*
* @readonly always three
*/
export const limit = 3
✅ Correct
/**
* The retry limit, always three.
*
* @readonly
*/
export const limit = 3
jsdoc/escape-inline-tags
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Forbid an unescaped @name inside the text of a JSDoc block.
Rule Details
Documentation tools read an @ followed by a word as the start of a tag, so @scope in the middle of a sentence can split the description or create a bogus tag. This rule reports an @ that starts a word, at the start of a line or after whitespace, in a block's description and in the text of its tags, except inside @example. The message names the three ways to write it: as an inline tag {@name}, escaped as \@name, or in backticks. Names in the allowed list are left alone. It does not fix.
Reimplements eslint-plugin-jsdoc's escape-inline-tags.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
allowedInlineTags | string[] | [] | Names, without @, that may appear unescaped, such as "scope". |
The original's options that enable a fixer and choose its escaping style are not read.
Examples
❌ Incorrect
/**
* Publishes the packages under @acme to the registry.
*/
export function publish(): void {
return undefined
}
✅ Correct
/**
* Publishes the packages under `@acme` to the registry.
*/
export function publish(): void {
return undefined
}
/**
* Publishes the packages under @acme to the registry.
*/
export function publish(): void {
return undefined
}
jsdoc/implements-on-classes
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Forbid @implements on a function that is not a class or constructor.
Rule Details
@implements says that a class fulfils an interface, so on a plain function it documents nothing true. This rule reports each @implements tag in the JSDoc block of a function, method or arrow function, unless the function is a class constructor or its block also carries @class or @constructor. Blocks on classes are not checked, since that is where the tag belongs. It does not fix.
Reimplements eslint-plugin-jsdoc's implements-on-classes.
Options
This rule has no options. The original's contexts option, which adds node types to check, is not read.
Examples
❌ Incorrect
/**
* Creates a logger.
*
* @implements {Logger}
*/
export function createLogger(): Logger {
return console
}
✅ Correct
/**
* Writes to the console.
*
* @implements {Logger}
*/
export class ConsoleLogger implements Logger {
log(message: string): void {
console.log(message)
}
}
/**
* Creates a logger.
*
* @class
* @implements {Logger}
*/
export function LegacyLogger(this: Logger): void {
return undefined
}
jsdoc/informative-docs
Reads TypeScript, JavaScriptNo preset: turn it on by id
Forbid JSDoc descriptions that only repeat the name of what they document.
Rule Details
"The user id" on a property called userId tells the reader nothing the code did not. This rule splits a block's description and each tag's description into words, camel case included, and removes the names of the documented declaration (and, for a class member, of its class), the tag's own name, and the filler words "a", "an", "i", "in", "of", "s" and "the", with "a" also covering "an" and "our". When no word is left, the description is reported. Empty descriptions pass; other rules require them. It does not fix.
Reimplements eslint-plugin-jsdoc's informative-docs.
Options
This rule has no options. The original's options, which set the aliases, the filler words and the tags to skip, are not read: the defaults apply.
Examples
❌ Incorrect
/**
* The user id.
*
* @param name - The name
*/
export function userId(name: string): string {
return name
}
✅ Correct
/**
* Derives the identifier a user signs in with.
*
* @param name - The display name, lower-cased before use
*/
export function userId(name: string): string {
return name
}
jsdoc/no-defaults
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset JSDOC_RULES
Forbid default values in a function's JSDoc, where they belong in the signature.
Rule Details
A default written in the docs, as @param [retries=3] or @default 3, duplicates the default in the signature and drifts from it. On the JSDoc blocks of functions, this rule reports a @param, @arg or @argument whose bracketed name carries =value, and a @default or @defaultvalue tag with a value. The fix drops the value: the parameter keeps its brackets, [retries], and the default tag is left bare.
Reimplements eslint-plugin-jsdoc's no-defaults.
Options
This rule has no options. The original's options, which add node types to check and also forbid the optional brackets, are not read.
Examples
❌ Incorrect
/**
* Fetches a resource.
*
* @param url - The address
* @param [retries=3] - How often to try again
*/
export function load(url: string, retries = 3): string {
return url.repeat(retries)
}
✅ Correct
/**
* Fetches a resource.
*
* @param url - The address
* @param [retries] - How often to try again
*/
export function load(url: string, retries = 3): string {
return url.repeat(retries)
}
jsdoc/no-types
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset JSDOC_RULES
Forbid types in the @param and @returns tags of TypeScript functions.
Rule Details
In TypeScript the signature already carries the types, so a type in braces in the JSDoc repeats it and can contradict it. This rule reports a type on a @param, @arg, @argument, @returns or @return tag in the JSDoc of a function, method or method signature, and a type on a @property or @prop tag in the JSDoc of a class. The fix removes the type and the space after the tag, across every line the type spans.
Reimplements eslint-plugin-jsdoc's no-types.
Options
This rule has no options. The original's contexts option, which adds node types to check, is not read.
Examples
❌ Incorrect
/**
* Adds two numbers.
*
* @param {number} a - The first
* @param {number} b - The second
* @returns {number} The sum
*/
export function add(a: number, b: number): number {
return a + b
}
✅ Correct
/**
* Adds two numbers.
*
* @param a - The first
* @param b - The second
* @returns The sum
*/
export function add(a: number, b: number): number {
return a + b
}
jsdoc/reject-any-type
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Forbid any and * in the types of JSDoc tags.
Rule Details
A JSDoc type of {any} or {*} says only that the value could be anything, which documents nothing. This rule reads the type of every typed tag in every JSDoc block and reports each any or * it contains, at any depth, such as {Array<any>}. It does not fix.
Reimplements eslint-plugin-jsdoc's reject-any-type.
Options
This rule has no options.
Examples
❌ Incorrect
/**
* The parsed payload.
*
* @type {Array<any>}
*/
export const payload = []
✅ Correct
/**
* The parsed payload.
*
* @type {Array<string>}
*/
export const payload = []
jsdoc/reject-function-type
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Forbid Function in the types of JSDoc tags.
Rule Details
{Function} accepts any callable and says nothing about its parameters or result. This rule reads the type of every typed tag in every JSDoc block and reports each Function it contains, at any depth, such as {Array<Function>}. A function type with its signature, {(value: string) => void}, passes. It does not fix.
Reimplements eslint-plugin-jsdoc's reject-function-type.
Options
This rule has no options.
Examples
❌ Incorrect
/**
* The registered listeners.
*
* @type {Array<Function>}
*/
export const listeners = []
✅ Correct
/**
* The registered listeners.
*
* @type {Array<(event: string) => void>}
*/
export const listeners = []
jsdoc/require-description
Reads TypeScript, JavaScriptNo preset: turn it on by id
Require a function's JSDoc block to open with a plain description.
Rule Details
The description is what a reader and an editor tooltip show first, so a block made of tags alone leaves the function unexplained. This rule reports the JSDoc block of a function, method, getter, setter or constructor that has no description before its tags. When the text sits in a @desc or @description tag instead, the message says to drop the tag or add text above it. A block tagged @ignore, @override, @implements or @inheritDoc, or a member of a class whose block carries one of the first three, is exempt. It does not fix.
Reimplements eslint-plugin-jsdoc's require-description.
Options
This rule has no options. The original's options, which choose the description style, add node types to check, exempt more tags and skip constructors, getters or setters, are not read: plain descriptions are required on every kind of function.
Examples
❌ Incorrect
/**
* @param path - Where the file is
* @returns Its contents
*/
export function read(path: string): string {
return path
}
✅ Correct
/**
* Reads a text file.
*
* @param path - Where the file is
* @returns Its contents
*/
export function read(path: string): string {
return path
}
jsdoc/require-jsdoc
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset JSDOC_RULES
Require a JSDoc block on function declarations and on the declarations the options name.
Rule Details
A missing block leaves a declaration without the description and tags that other JSDoc rules check and that editors show. This rule reports every function declaration that has no JSDoc block, and every declaration in a context the options list. A block on the first overload signature documents the implementation and the other overloads. Constructors, getters and setters are checked unless the options turn them off. The fix inserts an empty block before the declaration, or before its first decorator, at the declaration's indentation. A declaration that two listed contexts both match is reported twice, but fixed once.
Reimplements eslint-plugin-jsdoc's require-jsdoc.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
contexts | context name[] | [] | Further declarations that need a block, from the list below. |
checkConstructors | boolean | true | Check constructors. |
exemptEmptyConstructors | boolean | false | Let a constructor go when it has no parameters and returns no value. |
checkGetters | boolean | true | Check getters. |
checkSetters | boolean | 'no-getter' | true | Check setters, or with 'no-getter' only those without a matching getter. |
The context names are 'exported-function', 'exported-default-function', 'exported-class', 'exported-default-class', 'exported-arrow-variable' and 'exported-function-variable' (a function assigned to an exported const), 'interface', 'type-alias', 'property-signature', 'method-signature', 'public-method' (excluding an implementation that follows an overload signature), 'public-method-signature', 'public-property', 'public-abstract-method' and 'public-abstract-property'. A member is public unless it carries the private modifier or has a #private name.
The original's contexts are AST selectors and its require map chooses node types; this rule takes the named contexts above instead and always checks function declarations. The original's options for public-only checks, minimum line counts, empty functions and a custom fixer are not read, and its empty-constructor exemption defaults to on where this one defaults to off.
Examples
❌ Incorrect
function parse(text: string): number {
return Number(text)
}
export interface Point {
x: number
}
✅ Correct
/**
* Reads a number from text.
*
* @param text - The digits
* @returns The number
*/
function parse(text: string): number {
return Number(text)
}
export const double = (value: number): number => value * 2
jsdoc/require-next-type
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require a type on every @next tag.
Rule Details
The @next tag documents the values a caller sends into a generator through next(), and without a type it says nothing about them. This rule reports each JSDoc block in the file that holds a @next tag written without a type in braces, once per block. It does not fix.
Reimplements eslint-plugin-jsdoc's require-next-type.
Options
This rule has no options.
Examples
❌ Incorrect
/**
* Accumulates the numbers sent in.
*
* @next The number to add
*/
export function* total(): Generator<number, void, number> {
let sum = 0
while (true) sum += yield sum
}
✅ Correct
/**
* Accumulates the numbers sent in.
*
* @next {number} The number to add
*/
export function* total(): Generator<number, void, number> {
let sum = 0
while (true) sum += yield sum
}
jsdoc/require-param
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset JSDOC_RULES
Require a @param tag for every parameter of a documented function.
Rule Details
A JSDoc block that documents some parameters and skips others leaves the reader guessing about the rest. On functions, methods and constructors that have a JSDoc block, this rule reports each parameter without a @param tag. A destructured parameter needs a tag for its root and one for each property, as @param options.port; an unnamed root is called root0, root1 and so on, unless a tag at its position already names it. A property under a documented path whose type is neither absent nor a plain object is not required. A this parameter is never required.
Getters and setters are not checked, and neither is a block tagged @type, @ignore, @override, @implements or @inheritDoc, or a member of a class whose block carries one of the middle three. The fix inserts the missing tags in parameter order, before the tag of the next documented parameter or after the last @param, typed {...any} for a rest parameter. A function without a JSDoc block is left to require-jsdoc.
Reimplements eslint-plugin-jsdoc's require-param.
Options
This rule has no options. The original's options, which control the destructuring checks, the checked member kinds, the unnamed root name, the exempting tags, the node types checked and the fixers, are not read: the behaviour is fixed to the original's defaults.
Examples
❌ Incorrect
/**
* Connects to a server.
*
* @param host - The host name
*/
export function connect(host: string, { port, secure }: Options): void {
return undefined
}
✅ Correct
/**
* Connects to a server.
*
* @param host - The host name
* @param root0 - The connection settings
* @param root0.port - The port
* @param root0.secure - Whether to use TLS
*/
export function connect(host: string, { port, secure }: Options): void {
return undefined
}
jsdoc/require-param-description
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require every @param tag in a function's JSDoc to have a description.
Rule Details
A @param tag that gives only a name tells the reader nothing the signature did not. On the JSDoc blocks of functions and methods, this rule reports each @param tag whose description is empty. It does not fix.
Reimplements eslint-plugin-jsdoc's require-param-description.
Options
This rule has no options. The original's options, which add node types to check and set a default description for destructured roots, are not read.
Examples
❌ Incorrect
/**
* Resizes an image.
*
* @param width
*/
export function resize(width: number): number {
return width
}
✅ Correct
/**
* Resizes an image.
*
* @param width - The new width in pixels
*/
export function resize(width: number): number {
return width
}
jsdoc/require-param-name
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require every @param tag in a function's JSDoc to name its parameter.
Rule Details
A @param tag without a name cannot be matched to a parameter, so tools drop it or attach it to the wrong one. On the JSDoc blocks of functions and methods, this rule reports each @param tag that has no name, whether the tag is bare or carries only a type such as @param {string}. It does not fix.
Reimplements eslint-plugin-jsdoc's require-param-name.
Options
This rule has no options. The original's contexts option, which adds node types to check, is not read.
Examples
❌ Incorrect
/**
* Greets someone.
*
* @param {string}
*/
export function greet(name: string): string {
return name
}
✅ Correct
/**
* Greets someone.
*
* @param {string} name - Who to greet
*/
export function greet(name: string): string {
return name
}
jsdoc/require-property
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset JSDOC_RULES
Require a @typedef or @namespace of a plain object type to list its properties.
Rule Details
A @typedef {object} without @property tags declares an object type and says nothing about its shape. In every JSDoc block of the file, this rule reports a @typedef or @namespace whose type is object, Object or PlainObject when the block holds no @property tag. The fix adds an empty @property line after the block's last tag, to be filled in.
Reimplements eslint-plugin-jsdoc's require-property.
Options
This rule has no options.
Examples
❌ Incorrect
/**
* A server address.
*
* @typedef {object} Address
*/
✅ Correct
/**
* A server address.
*
* @typedef {object} Address
* @property {string} host - The host name
* @property {number} port - The port
*/
/**
* A port number.
*
* @typedef {number} Port
*/
jsdoc/require-property-description
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require every @property tag to have a description.
Rule Details
A @property tag that gives only a name and type documents the shape but not the meaning of the property. In every JSDoc block of the file, this rule reports each @property tag whose description is empty. It does not fix.
Reimplements eslint-plugin-jsdoc's require-property-description.
Options
This rule has no options.
Examples
❌ Incorrect
/**
* A server address.
*
* @typedef {object} Address
* @property {string} host
*/
✅ Correct
/**
* A server address.
*
* @typedef {object} Address
* @property {string} host - The host name, without a port
*/
jsdoc/require-property-name
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require every @property tag to name its property.
Rule Details
A @property tag without a name cannot be matched to a member, so documentation tools drop it. In every JSDoc block of the file, this rule reports each @property tag that has no name, whether it is bare or carries only a type such as @property {string}. It does not fix.
Reimplements eslint-plugin-jsdoc's require-property-name.
Options
This rule has no options.
Examples
❌ Incorrect
/**
* A server address.
*
* @typedef {object} Address
* @property {string}
*/
✅ Correct
/**
* A server address.
*
* @typedef {object} Address
* @property {string} host - The host name
*/
jsdoc/require-returns
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require a @returns tag, once, in the JSDoc of a function that returns a value.
Rule Details
When a documented function gives back a value, the reader needs to know what it is. On functions and methods that have a JSDoc block, this rule reports a block with no @returns tag when the function returns a value on some path, or has an expression body, or, for a declared signature, names a return type other than void, undefined or never. A returned new Promise counts only when its executor resolves a value. It also reports a block with more than one @returns tag. Constructors are skipped, and so are blocks tagged @abstract, @virtual, @class, @constructor, @type, @interface, @ignore, @override, @implements or @inheritDoc, and members of a class whose block carries one of @ignore, @override or @implements. Getters and setters are checked. It does not fix.
Reimplements eslint-plugin-jsdoc's require-returns.
Options
This rule has no options. The original's options, which force the tag on async functions, check constructors or skip getters, add node types and exempt more tags, are not read: the behaviour is fixed to the original's defaults.
Examples
❌ Incorrect
/**
* Reads the setting.
*
* @param key - The setting's name
*/
export function read(key: string): string {
return key
}
✅ Correct
/**
* Reads the setting.
*
* @param key - The setting's name
* @returns Its value
*/
export function read(key: string): string {
return key
}
jsdoc/require-returns-check
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require a function documented with @returns to return a value.
Rule Details
A @returns tag promises callers a value, so a function that returns nothing contradicts its docs. On documented functions and methods, this rule reports a block with more than one @returns tag. With a single tag it reports the block when the function never returns a value, for an untyped tag, or when some path returns without a value, for a typed one. A type that admits undefined or void, such as {string | undefined}, an assertion type such as {asserts value}, and generator functions are exempt. A {never} tag is reported when the function does return a value. Async functions are skipped, since their value is the promise's, and so are constructors, blocks marked @abstract, @virtual, @class, @constructor or @interface, and members of a class documented as an @interface. It does not fix.
Reimplements eslint-plugin-jsdoc's require-returns-check.
Options
This rule has no options. The original's options, which stop exempting async functions and generators and report a missing return for undefined types, are not read: the behaviour is fixed to the original's defaults.
Examples
❌ Incorrect
/**
* Logs a message.
*
* @param message - What to log
* @returns The message
*/
export function log(message: string): void {
console.log(message)
}
✅ Correct
/**
* Logs a message.
*
* @param message - What to log
* @returns The message
*/
export function log(message: string): string {
console.log(message)
return message
}
jsdoc/require-returns-description
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require every @returns tag in a function's JSDoc to have a description.
Rule Details
A @returns tag with no description says that something comes back but not what it means. On the JSDoc blocks of functions and methods, this rule reports each @returns tag whose description is empty, unless its type says nothing comes back: {void}, {undefined}, {Promise<void>} or {Promise<undefined>}. It does not fix.
Reimplements eslint-plugin-jsdoc's require-returns-description.
Options
This rule has no options. The original's contexts option, which adds node types to check, is not read.
Examples
❌ Incorrect
/**
* Counts the items.
*
* @returns
*/
export function count(): number {
return 0
}
✅ Correct
/**
* Counts the items.
*
* @returns How many items the list holds
*/
export function count(): number {
return 0
}
/**
* Clears the items.
*
* @returns {void}
*/
export function clear(): void {
return undefined
}
jsdoc/require-throws-type
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require a type on every @throws tag.
Rule Details
A caller deciding what to catch needs to know what is thrown, and a @throws tag without a type only says that something might be. This rule reports each JSDoc block in the file that holds a @throws or @exception tag written without a type in braces, once per block. It does not fix.
Reimplements eslint-plugin-jsdoc's require-throws-type.
Options
This rule has no options.
Examples
❌ Incorrect
/**
* Parses a port number.
*
* @param text - The digits
* @returns The port
* @throws When the text is not a valid port
*/
export function port(text: string): number {
return Number(text)
}
✅ Correct
/**
* Parses a port number.
*
* @param text - The digits
* @returns The port
* @throws {RangeError} When the text is not a valid port
*/
export function port(text: string): number {
return Number(text)
}
jsdoc/require-yields
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require a @yields tag, once, in the JSDoc of a generator that yields a value.
Rule Details
When a documented generator produces values, the reader needs to know what they are. On functions and methods that have a JSDoc block, this rule reports a block with no @yields tag when the function is a generator that yields a value, or when the block is marked @generator. It also reports a block with more than one @yields tag. Constructors are skipped, and so are blocks tagged @abstract, @virtual, @class, @constructor, @type, @interface, @ignore, @override, @implements or @inheritDoc, and members of a class whose block carries one of @ignore, @override or @implements. It does not fix.
Reimplements eslint-plugin-jsdoc's require-yields.
Options
This rule has no options. The original's options, which require @next tags, force the tag on generators that yield nothing, add node types and exempt more tags, are not read: the behaviour is fixed to the original's defaults.
Examples
❌ Incorrect
/**
* Counts upwards from zero.
*/
export function* count(): Generator<number> {
for (let n = 0; ; n += 1) yield n
}
✅ Correct
/**
* Counts upwards from zero.
*
* @yields The next number
*/
export function* count(): Generator<number> {
for (let n = 0; ; n += 1) yield n
}
jsdoc/require-yields-check
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require a function documented with @yields to yield a value.
Rule Details
A @yields tag promises values to whoever iterates the generator, so a function that yields nothing contradicts its docs. On documented functions and methods, this rule reports a block with more than one @yields tag, and a block whose single @yields tag stands over a function that yields no value, unless the tag's type admits undefined or void. A {never} tag is reported when the function does yield a value. Constructors, blocks marked @abstract, @virtual, @class, @constructor or @interface, and members of a class documented as an @interface are skipped. It does not fix.
Reimplements eslint-plugin-jsdoc's require-yields-check.
Options
This rule has no options. The original's options, which limit the check to generators, add node types and check @next tags as well, are not read.
Examples
❌ Incorrect
/**
* Walks the list.
*
* @yields Each item
*/
export function* walk(items: string[]): Generator<string> {
return items.length
}
✅ Correct
/**
* Walks the list.
*
* @yields Each item
*/
export function* walk(items: string[]): Generator<string> {
yield* items
}
jsdoc/require-yields-type
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require a type on every @yields tag.
Rule Details
A @yields tag without a type says that a generator produces values but not what they are. This rule reports each JSDoc block in the file that holds a @yields or @yield tag written without a type in braces, once per block. It does not fix.
Reimplements eslint-plugin-jsdoc's require-yields-type.
Options
This rule has no options.
Examples
❌ Incorrect
/**
* Counts upwards from zero.
*
* @yields The next number
*/
export function* count(): Generator<number> {
for (let n = 0; ; n += 1) yield n
}
✅ Correct
/**
* Counts upwards from zero.
*
* @yields {number} The next number
*/
export function* count(): Generator<number> {
for (let n = 0; ; n += 1) yield n
}
jsdoc/ts-no-empty-object-type
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Forbid the empty object type {} in the types of JSDoc tags.
Rule Details
The type {} admits every value except null and undefined, literals included, which is rarely what a block means: object says "any object" and unknown says "any value". This rule reads the type of every typed tag in every JSDoc block and reports each empty object type it contains, at any depth, such as {Array<{}>}. An object type with members passes. It does not fix.
Reimplements eslint-plugin-jsdoc's ts-no-empty-object-type, the JSDoc counterpart of typescript/no-empty-object-type.
Options
This rule has no options.
Examples
❌ Incorrect
/**
* The cached entries.
*
* @type {Array<{}>}
*/
export const entries = []
✅ Correct
/**
* The cached entries.
*
* @type {Array<{key: string}>}
*/
export const entries = []
jsdoc/valid-types
Reads TypeScript, JavaScriptPreset JSDOC_RULES
Require the types, names and namepaths in JSDoc tags to be well formed and to sit on tags that take them.
Rule Details
A type that does not parse, or a name on a tag that takes none, is silently misread by documentation tools and editors. In every JSDoc block of the file, this rule reports, stopping at the first problem of each tag:
- a malformed bracketed name: unpaired brackets, an empty name, an empty default, or a default containing an unquoted
= - a name or a bracketed type on a tag that takes none, such as
@private {string} - a missing name, type, or either one where the tag needs it, such as
@fireswithout an event name or@typewithout a type - a type that does not parse as a TypeScript-flavoured JSDoc type, such as
{Array<string} - a namepath with a stray character on tags such as
@memberof,@module,@propertyor@template, and a@borrowswithout anastarget - an inline tag such as
{@link}with no content, in a description or a tag's text
Missing @param and @property names are left to jsdoc/require-param-name and jsdoc/require-property-name. It does not fix.
Reimplements eslint-plugin-jsdoc's valid-types.
Options
This rule has no options. The original's allowEmptyNamepaths option is not read: empty namepaths are reported where a tag needs one.
Examples
❌ Incorrect
/**
* Lists the names.
*
* @param {Array<string} names - The names
* @returns The joined names
*/
export function list(names: string[]): string {
return names.join(', ')
}
/**
* The default port.
*
* @private {number}
*/
export const port = 8080
/**
* See {@link} for details.
*/
export const docs = true
✅ Correct
/**
* Lists the names, see {@link join}.
*
* @param {Array<string>} names - The names
* @returns The joined names
*/
export function list(names: string[]): string {
return names.join(', ')
}
Limitations
The original reads tags by its mode setting; this rule always reads them in TypeScript mode, so tag structures that only the closure or plain JSDoc modes allow are reported.
React rules
Reimplemented from eslint-plugin-react. REACT_RULES turns on the ones its recommended configuration does.
| Rule | Reads | 🔧 | REACT_RULES |
|---|---|---|---|
react/display-name | TypeScript, JavaScript | ✅ | |
react/jsx-key | TypeScript, JavaScript | ✅ | |
react/jsx-no-comment-textnodes | TypeScript, JavaScript | ✅ | |
react/jsx-no-duplicate-props | TypeScript, JavaScript | ✅ | |
react/jsx-no-target-blank | TypeScript, JavaScript | 🔧 | ✅ |
react/jsx-no-undef | TypeScript, JavaScript | ✅ | |
react/jsx-uses-react | TypeScript, JavaScript | ✅ | |
react/jsx-uses-vars | TypeScript, JavaScript | ✅ | |
react/no-children-prop | TypeScript, JavaScript | ✅ | |
react/no-danger-with-children | TypeScript, JavaScript | ✅ | |
react/no-deprecated | TypeScript, JavaScript | ✅ | |
react/no-direct-mutation-state | TypeScript, JavaScript | ✅ | |
react/no-find-dom-node | TypeScript, JavaScript | ✅ | |
react/no-is-mounted | TypeScript, JavaScript | ✅ | |
react/no-render-return-value | TypeScript, JavaScript | ✅ | |
react/no-string-refs | TypeScript, JavaScript | ✅ | |
react/no-unescaped-entities | TypeScript, JavaScript | ✅ | |
react/no-unknown-property | TypeScript, JavaScript | 🔧 | ✅ |
react/require-render-return | TypeScript, JavaScript | ✅ |
react/display-name
Reads TypeScript, JavaScriptPreset REACT_RULES
Require every React component to have a display name.
Rule Details
A component without a name shows as anonymous in React's developer tools and in error stacks. This rule finds components the way React sees them: class components, createReactClass objects, and functions that return JSX or null from a position a component can take, including those wrapped in memo or forwardRef. A component counts as named when it is declared or assigned under a name, when its displayName is set, or when a factory object lists it. An anonymous default export and an anonymous function handed to memo or forwardRef are reported. It does not fix.
The rule reads the shared settings. The pragma ({ react: { pragma: 'Preact' } }, or a @jsx comment in the file) names the object memo and forwardRef are read from, and { componentWrapperFunctions: [...] } adds wrapper functions, as names or as { property, object } objects. With a React version ({ react: { version: '16.12' } }) of 0.14.10 and later 0.14, 15.7 and later 15, or 16.12 and newer, the default when no version is set, a wrapper call whose argument is itself a call, such as memo(forwardRef(...)), is not reported, because those versions name it from the inner component.
Reimplements eslint-plugin-react's display-name.
Options
This rule has no options. The original's options, which ignore names the transpiler infers and check legacy context objects, are not read: inferred names count, and context objects are not checked.
Examples
❌ Incorrect
import { forwardRef, memo } from 'react'
export const Card = memo(() => <div className="card" />)
export const Input = forwardRef<HTMLInputElement>((props, ref) => <input ref={ref} />)
✅ Correct
import { forwardRef, memo } from 'react'
export const Card = memo(function Card() {
return <div className="card" />
})
const Input = forwardRef<HTMLInputElement>((props, ref) => <input ref={ref} />)
Input.displayName = 'Input'
react/jsx-key
Reads TypeScript, JavaScriptPreset REACT_RULES
Require a key prop on JSX elements in arrays and iterator callbacks.
Rule Details
React tells list items apart by their key; without one it falls back to the index, which mixes up state and DOM nodes when the list is reordered. This rule reports a JSX element without a key attribute when it sits directly in an array literal, or when it is returned by the callback of a .map() call or the second argument of Array.from(). In a callback it follows an expression body, both branches of a conditional, the right side of a logical expression, and the returns of a block body through if statements. A key passed only through a spread does not count. Nothing inside a Children.toArray() call is checked, since it assigns keys itself; the pragma setting ({ react: { pragma: 'Preact' } }, or a @jsx comment) names the object Children may be read from. It does not fix.
Reimplements eslint-plugin-react's jsx-key.
Options
This rule has no options. The original's options, which check fragment shorthands, require the key before a spread and warn on duplicate keys, are not read: shorthand fragments are not checked.
Examples
❌ Incorrect
const items = ['a', 'b'].map((label) => <li>{label}</li>)
const cells = [<td>1</td>, <td>2</td>]
✅ Correct
const items = ['a', 'b'].map((label) => <li key={label}>{label}</li>)
const cells = [<td key="one">1</td>, <td key="two">2</td>]
react/jsx-no-comment-textnodes
Reads TypeScript, JavaScriptPreset REACT_RULES
Forbid text in JSX children that looks like a comment.
Rule Details
Inside JSX children, // note and /* note */ are not comments: they render as text on the page. This rule reports a JSX text node in which any line, leading whitespace aside, starts with // or /*. A comment in JSX belongs in braces, as {/* note */}. It does not fix.
Reimplements eslint-plugin-react's jsx-no-comment-textnodes.
Options
This rule has no options.
Examples
❌ Incorrect
const banner = (
<header>
// shown only on the home page
<h1>Welcome</h1>
</header>
)
✅ Correct
const banner = (
<header>
{/* shown only on the home page */}
<h1>Welcome</h1>
</header>
)
react/jsx-no-duplicate-props
Reads TypeScript, JavaScriptPreset REACT_RULES
Forbid naming the same prop twice on one JSX element.
Rule Details
When a JSX element repeats an attribute, only the last value takes effect, so the earlier one is dead code and usually a merge or copy mistake. This rule reports every attribute after the first with the same name, compared case-sensitively. Spread attributes and namespaced attributes such as xlink:href are not compared. It does not fix.
Reimplements eslint-plugin-react's jsx-no-duplicate-props.
Options
This rule has no options. The original's ignoreCase option is not read: names that differ only in case are different props.
Examples
❌ Incorrect
const field = <input name="email" type="text" name="mail" />
✅ Correct
const field = <input name="email" type="text" />
react/jsx-no-target-blank
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset REACT_RULES
Forbid target="_blank" on external links without rel="noreferrer".
Rule Details
A link that opens another site in a new tab hands that page a handle to the opening window in older browsers, which it can use to navigate the original tab elsewhere. rel="noreferrer", which implies rel="noopener", cuts the handle. This rule reports an <a> element whose last target= attribute may be "_blank", as a literal in any case or as either branch of a conditional, when its href= is an external literal (one starting with a scheme such as https: or with //) or any expression, and its rel= does not include noreferrer in every value it may take.
The fix adds rel="noreferrer" after the last attribute, gives a bare rel attribute that value, or appends noreferrer to a literal value. It is withheld when a spread attribute after the target, or a spread with no rel attribute at all, could supply the value, and when the rel value is an expression.
Reimplements eslint-plugin-react's jsx-no-target-blank.
Options
This rule has no options. The original's options, which allow the referrer, skip dynamic links, warn on spreads and check forms, are not read: the behaviour is fixed to the original's defaults.
Examples
❌ Incorrect
const docs = (
<a href="https://example.com/docs" target="_blank">
Docs
</a>
)
const profile = (url: string) => (
<a href={url} target="_blank" rel="noopener">
Profile
</a>
)
✅ Correct
const docs = (
<a href="https://example.com/docs" target="_blank" rel="noreferrer">
Docs
</a>
)
const help = (
<a href="/help" target="_blank">
Help
</a>
)
Limitations
Only the <a> element is checked: the original's link components setting, which adds custom link components and their address props, is not read.
react/jsx-no-undef
Reads TypeScript, JavaScriptPreset REACT_RULES
Forbid JSX elements whose component is not declared.
Rule Details
A JSX element names a variable, and an undeclared one fails only when the element renders, usually because an import is missing or misspelt. This rule reports a capitalised element name, or the root object of a member name such as <Form.Field>, that no scope from the element out to the module declares. Lower-case names are DOM elements and namespaced names such as svg:rect are left alone. In a module, a file with imports or exports, globals do not count; in a script they do. It does not fix.
Reimplements eslint-plugin-react's jsx-no-undef.
Options
This rule has no options. The original's allowGlobals option is not read: a module's elements never resolve to globals.
Examples
❌ Incorrect
import { Button } from './button'
export const toolbar = (
<Toolbar>
<Button />
</Toolbar>
)
✅ Correct
import { Button } from './button'
import { Toolbar } from './toolbar'
export const toolbar = (
<Toolbar>
<Button />
</Toolbar>
)
react/jsx-uses-react
Reads TypeScript, JavaScriptPreset REACT_RULES
Mark the JSX pragma, and the fragment component, as used wherever JSX appears.
Rule Details
With the classic JSX transform, every element compiles to a call on the pragma, React.createElement by default, so an import React from 'react' that the code never names is still needed. This rule reports nothing itself: it marks the pragma variable as used at every JSX element, and the pragma and the fragment component at every <> fragment, so that typescript/no-unused-vars does not report them. It only has an effect when that rule runs too.
The pragma comes from a @jsx comment in the file, then the settings ({ react: { pragma: 'h' } }), then React. The fragment's name comes from the settings ({ react: { fragment: 'Frag' } }), then Fragment. With the automatic JSX runtime, React 17 and later, the import is not needed and this rule can stay off.
Reimplements eslint-plugin-react's jsx-uses-react.
Options
This rule has no options.
Examples
✅ Correct
import React from 'react'
export const greeting = <p>Hello</p>
react/jsx-uses-vars
Reads TypeScript, JavaScriptPreset REACT_RULES
Mark variables named as JSX elements as used.
Rule Details
A component referenced only as <Button /> is a use of the variable Button, but a generic unused-variable check does not see JSX element names as references. This rule reports nothing itself: it marks the variable an element names as used, or the root object of a member name such as <Form.Field>, so that typescript/no-unused-vars does not report it. Lower-case and namespaced names are DOM elements and mark nothing. It only has an effect when that rule runs too.
Reimplements eslint-plugin-react's jsx-uses-vars.
Options
This rule has no options.
Examples
✅ Correct
import { Button } from './button'
export const toolbar = <Button />
react/no-children-prop
Reads TypeScript, JavaScriptPreset REACT_RULES
Forbid passing children through a children prop.
Rule Details
Children belong between an element's tags, or after the props argument of createElement, where every reader expects them. This rule reports a children attribute on a JSX element, and a createElement call whose props object literal has a children key. The call counts when it is read off the pragma, as React.createElement, or when a bare createElement was imported or destructured from it. The pragma comes from a @jsx comment in the file, then the settings ({ react: { pragma: 'h' } }), then React. It does not fix.
Reimplements eslint-plugin-react's no-children-prop.
Options
This rule has no options. The original's allowFunctions option is not read: a function passed as the children prop is reported too.
Examples
❌ Incorrect
import { createElement } from 'react'
const note = <Note children="Saved" />
const card = createElement('div', { children: 'Saved' })
✅ Correct
import { createElement } from 'react'
const note = <Note>Saved</Note>
const card = createElement('div', {}, 'Saved')
react/no-danger-with-children
Reads TypeScript, JavaScriptPreset REACT_RULES
Forbid setting both children and dangerouslySetInnerHTML on one element.
Rule Details
dangerouslySetInnerHTML replaces an element's content, so children given alongside it are either lost or make React throw. This rule reports a JSX element that has the HTML prop and also has children, or a children prop; whitespace that only spans a line break does not count as a child. It also reports a .createElement() call whose props carry the HTML prop while children are passed as further arguments or as a prop. Props are followed through spread attributes and identifiers to the object literal a variable is initialised with. It does not fix.
Reimplements eslint-plugin-react's no-danger-with-children.
Options
This rule has no options.
Examples
❌ Incorrect
const html = { __html: '<b>Saved</b>' }
const note = <div dangerouslySetInnerHTML={html}>Saved</div>
✅ Correct
const html = { __html: '<b>Saved</b>' }
const note = <div dangerouslySetInnerHTML={html} />
const plain = <div>Saved</div>
Limitations
Any .createElement() call is checked, whatever object it is read from; a bare createElement call is not.
react/no-deprecated
Reads TypeScript, JavaScriptPreset REACT_RULES
Forbid React APIs that the configured React version deprecates.
Rule Details
Deprecated APIs warn at run time and disappear in a later major version. This rule reports a deprecated API when it is read as a member, such as ReactDOM.render, imported by name from 'react', 'react-dom', 'react-dom/server' or 'react-addons-perf', or destructured from one of those modules' objects or require calls. It also reports the legacy lifecycle methods componentWillMount, componentWillReceiveProps and componentWillUpdate on class components and createReactClass objects, from React 16.9. The message names the version, the replacement and, where there is one, a page explaining the change. The list runs from React 0.12 to the React 18 root API changes. It does not fix.
The rule reads the React version from the settings ({ react: { version: '17.0' } }) and reports only what that version already deprecates; without a version, or with 'detect', it assumes the newest and reports everything. The pragma ({ react: { pragma: 'Preact' } }, or a @jsx comment in the file) is the object the core APIs are read from.
Reimplements eslint-plugin-react's no-deprecated.
Options
This rule has no options.
Examples
❌ Incorrect
import { render } from 'react-dom'
import { Component } from 'react'
render(<App />, document.getElementById('root'))
export class Clock extends Component {
componentWillMount() {
this.setState({ now: Date.now() })
}
}
✅ Correct
import { createRoot } from 'react-dom/client'
import { Component } from 'react'
createRoot(document.getElementById('root')!).render(<App />)
export class Clock extends Component {
componentDidMount() {
this.setState({ now: Date.now() })
}
}
Limitations
The version is never detected from the installed package; a project on an older React must set it, or it sees reports for deprecations it has not reached.
react/no-direct-mutation-state
Reads TypeScript, JavaScriptPreset REACT_RULES
Forbid assigning to this.state outside the constructor of a class component.
Rule Details
React only re-renders when state changes through setState; writing to this.state directly leaves the screen stale and can be overwritten by the next update. In class components and createReactClass objects, this rule reports an assignment to this.state or to anything under it, and an increment or decrement of something under it. Inside the constructor, where state is set up, assignments pass, but those inside a callback passed to a call there are reported, since the callback runs later. It does not fix.
Reimplements eslint-plugin-react's no-direct-mutation-state.
Options
This rule has no options.
Examples
❌ Incorrect
import { Component } from 'react'
export class Counter extends Component<object, { count: number }> {
increment() {
this.state.count += 1
}
}
✅ Correct
import { Component } from 'react'
export class Counter extends Component<object, { count: number }> {
constructor(props: object) {
super(props)
this.state = { count: 0 }
}
increment() {
this.setState(({ count }) => ({ count: count + 1 }))
}
}
react/no-find-dom-node
Reads TypeScript, JavaScriptPreset REACT_RULES
Forbid calls to findDOMNode.
Rule Details
findDOMNode reaches through a component to its DOM node, which breaks the component's abstraction, does not work with function components, is deprecated in strict mode and was removed in React 19. A ref on the element is the replacement. This rule reports a call of a bare findDOMNode and of any member named findDOMNode, such as ReactDOM.findDOMNode(this). It does not fix.
Reimplements eslint-plugin-react's no-find-dom-node.
Options
This rule has no options.
Examples
❌ Incorrect
import { findDOMNode } from 'react-dom'
export function focus(component: React.Component) {
;(findDOMNode(component) as HTMLElement).focus()
}
✅ Correct
import { useRef } from 'react'
export function Field() {
const input = useRef<HTMLInputElement>(null)
return <input ref={input} onClick={() => input.current?.focus()} />
}
react/no-is-mounted
Reads TypeScript, JavaScriptPreset REACT_RULES
Forbid calls to this.isMounted().
Rule Details
isMounted is an anti-pattern: code that checks it usually hides a missing cleanup, such as a subscription or timer left running after unmount, and the method does not exist on ES6 class components. This rule reports a call of this.isMounted() inside an object property or a class method. It does not fix: cancel the pending work when the component unmounts instead.
Reimplements eslint-plugin-react's no-is-mounted.
Options
This rule has no options.
Examples
❌ Incorrect
import createReactClass from 'create-react-class'
export const Clock = createReactClass({
tick() {
if (this.isMounted()) this.setState({ now: Date.now() })
},
})
✅ Correct
import createReactClass from 'create-react-class'
export const Clock = createReactClass({
componentWillUnmount() {
clearInterval(this.timer)
},
})
react/no-render-return-value
Reads TypeScript, JavaScriptPreset REACT_RULES
Forbid using the return value of ReactDOM.render.
Rule Details
The legacy ReactDOM.render returns a reference to the root component instance, which future React versions do not provide and which is not ready during concurrent rendering; a ref callback is the supported way to reach it. This rule reports a render call on the renderer object when its value is used: assigned, stored in a variable or object property, returned, or the body of an arrow function. It does not fix.
The renderer object depends on the React version in the settings ({ react: { version: '0.14' } }): ReactDOM from 15 on and when no version is set, React or ReactDOM in 0.14, and React in 0.13.
Reimplements eslint-plugin-react's no-render-return-value.
Options
This rule has no options.
Examples
❌ Incorrect
import ReactDOM from 'react-dom'
const app = ReactDOM.render(<App />, document.getElementById('root'))
✅ Correct
import ReactDOM from 'react-dom'
ReactDOM.render(<App />, document.getElementById('root'))
react/no-string-refs
Reads TypeScript, JavaScriptPreset REACT_RULES
Forbid string refs.
Rule Details
String refs are a legacy API with known problems, such as attaching to the wrong owner, and React 19 removed them; useRef, createRef or a ref callback replaces them. This rule reports a ref attribute whose value is a string literal, quoted or in braces. For React versions before 18.3, set in the settings ({ react: { version: '16.14' } }), it also reports reading this.refs inside a class component or createReactClass object; with a newer version, or none set, that read is not reported. It does not fix.
Reimplements eslint-plugin-react's no-string-refs.
Options
This rule has no options. The original's noTemplateLiterals option is not read: a template literal used as a ref is not reported.
Examples
❌ Incorrect
const field = <input ref="email" />
✅ Correct
import { useRef } from 'react'
export function Field() {
const email = useRef<HTMLInputElement>(null)
return <input ref={email} />
}
react/no-unescaped-entities
Reads TypeScript, JavaScriptPreset REACT_RULES
Forbid characters in JSX text that are easily mistaken for markup.
Rule Details
A stray >, }, " or ' in JSX text is usually a typo for a closing tag, brace or attribute quote, and a reader cannot tell whether it is meant to render. This rule reports each occurrence of those four characters in JSX text, and the message lists the entities that escape it, such as > or ". Text inside braces, {'>'}, is a JavaScript string and passes. It does not fix.
Reimplements eslint-plugin-react's no-unescaped-entities.
Options
This rule has no options. The original's forbid option, which replaces the list of characters, is not read.
Examples
❌ Incorrect
const hint = <p>Don't close the tab, it's still "saving"</p>
✅ Correct
const hint = <p>Don't close the tab, it's still “saving”</p>
const quote = <p>{"Don't close the tab"}</p>
Limitations
The parser rejects a bare > or } in JSX text as a syntax error, as the TypeScript compiler does, so in practice the file fails to parse before this rule can report those two characters; only quotes reach it.
react/no-unknown-property
Reads TypeScript, JavaScript🔧 Fixable with --fixPreset REACT_RULES
Forbid attributes on DOM elements that React does not know.
Rule Details
React renders DOM attributes by its own names, className rather than class, so an HTML spelling or a typo is dropped or triggers a warning at run time. On lower-case DOM elements, this rule reports an attribute React does not recognise. An HTML or SVG name React renames, such as class, for or stroke-width, and a known name written in the wrong case, such as onclick, are fixed to React's name. An attribute React allows only on certain tags, such as crossOrigin outside media and link elements, is reported with the tags that take it. Data and ARIA attributes, components, member names, custom elements with a dash, elements with an is attribute, and the <fbt> and <fbs> tags are left alone.
The React version in the settings ({ react: { version: '16.0' } }) matters for two groups: allowTransparency is known only before 16.1, and pointer event handlers such as onPointerDown only from 16.4, which is the case when no version is set.
Reimplements eslint-plugin-react's no-unknown-property.
Options
This rule has no options. The original's options, which ignore chosen attributes and require lower-case data attributes, are not read.
Examples
❌ Incorrect
const label = <label class="field" for="email" onclick={focus} />
const icon = <svg stroke-width="2" foo="bar" />
✅ Correct
const label = <label className="field" htmlFor="email" onClick={focus} />
const icon = <svg strokeWidth="2" data-icon="check" aria-hidden="true" />
react/require-render-return
Reads TypeScript, JavaScriptPreset REACT_RULES
Require the render method of a class component to return something.
Rule Details
A render method without a return renders nothing, which is almost always a forgotten keyword after a refactor. This rule reports a render member whose value is a function with a block body and no return of its own; a return inside a nested function, such as a .map() callback, does not count. An arrow function with an expression body passes. Class components are classes that extend Component or PureComponent, bare or read off the pragma, and objects passed to the class factory, createReactClass unless the settings name another ({ react: { createClass: 'createClass' } }). It does not fix.
Reimplements eslint-plugin-react's require-render-return.
Options
This rule has no options.
Examples
❌ Incorrect
import { Component } from 'react'
export class List extends Component<{ items: string[] }> {
render() {
;<ul>
{this.props.items.map((item) => {
return <li key={item}>{item}</li>
})}
</ul>
}
}
✅ Correct
import { Component } from 'react'
export class List extends Component<{ items: string[] }> {
render() {
return (
<ul>
{this.props.items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
)
}
}
React hooks rules
Reimplemented from eslint-plugin-react-hooks. REACT_RULES turns both on.
| Rule | Reads | 🔧 | REACT_RULES |
|---|---|---|---|
react-hooks/exhaustive-deps | TypeScript, JavaScript | ✅ | |
react-hooks/rules-of-hooks | TypeScript, JavaScript | ✅ |
react-hooks/exhaustive-deps
Reads TypeScript, JavaScriptPreset REACT_RULES
Require the dependency arrays of React effect, memo and callback hooks to list exactly the reactive values their callback reads.
Rule Details
A hook such as useEffect reruns its callback only when a value in its dependency array changes. A value the callback reads but the array omits leaves the callback working with a stale copy; a value listed but not read reruns it for nothing. The rule checks the calls of useEffect, useLayoutEffect, useCallback, useMemo and useImperativeHandle, bare or off the React namespace, and reports:
- a missing dependency: a prop, state value or other value from the component's render scope that the callback reads and the array omits;
- an unnecessary or duplicate dependency, such as an outer constant or a value listed twice;
- an array that is not an array literal, or a callback that is not an inline function or a function declared in the component, whose dependencies cannot be checked;
- a dependency that changes on every render, such as an object or function created in the component body;
- an effect callback that is
async, aref.currentread in an effect cleanup, and an assignment to an outer variable that is lost after each render; - a state setter called by an effect with no dependency array, which loops, and a
useMemooruseCallbackwith no array, which does nothing.
Values known to be stable, such as the setter returned by useState, the dispatch of useReducer and a ref object from useRef, need not be listed. The message names the array the rule would write. The rule does not fix.
Reimplements eslint-plugin-react-hooks' exhaustive-deps.
Options
This rule has no options. The original's additionalHooks option is not read, so custom hooks are never checked, and neither are its experimental options.
Examples
❌ Incorrect
function Search({ query }: { query: string }) {
const [results, setResults] = useState([])
useEffect(() => {
find(query).then(setResults)
}, [])
return <List items={results} />
}
✅ Correct
function Search({ query }: { query: string }) {
const [results, setResults] = useState([])
useEffect(() => {
find(query).then(setResults)
}, [query])
return <List items={results} />
}
Limitations
The original offers suggestions that rewrite the dependency array; this rule names the array in its message instead.
react-hooks/rules-of-hooks
Reads TypeScript, JavaScriptPreset REACT_RULES
Require React hooks to be called unconditionally, at the top level of a component or a custom hook.
Rule Details
React matches hook calls to their state by call order, so every render must call the same hooks in the same order. A hook is a function whose name is "use", or starts with "use" followed by a capital letter or a digit, called bare or off a capitalised object (React.useState(...)). The rule follows the code paths of each function and reports a hook call that:
- runs only on some paths through a component or custom hook: inside a condition, after an early return, or behind
&&and?:; - may run more than once, in a loop;
- sits in an async component or hook;
- sits in a class method, in a function whose name is neither a component (capitalised) nor a hook, inside a callback nested in a component, or at the top level of the module.
A function passed to forwardRef or memo counts as a component. React's use() may be called conditionally and in loops, and is only checked for where it is called. The rule does not fix.
Reimplements eslint-plugin-react-hooks' rules-of-hooks.
Options
This rule has no options.
Examples
❌ Incorrect
function Profile({ id }: { id?: string }) {
if (!id) return null
const [user, setUser] = useState(null)
return <p>{user}</p>
}
function load() {
return useContext(Session)
}
✅ Correct
function Profile({ id }: { id?: string }) {
const [user, setUser] = useState(null)
if (!id) return null
return <p>{user}</p>
}
function useSession() {
return useContext(Session)
}
Limitations
The checks the original's newer releases make on functions returned by useEffectEvent are not made.
package.json rules
Reimplemented from eslint-plugin-package-json. No preset turns it on; name it by id.
| Rule | Reads | 🔧 |
|---|---|---|
package-json/restrict-dependency-ranges | JSON |
package-json/restrict-dependency-ranges
Reads JSONNo preset: turn it on by id
Require the dependency versions of a package.json to use the range types the configuration allows.
Rule Details
Whether a project pins its dependencies, takes caret ranges or takes tilde ranges is a policy, and a manifest that mixes them updates some packages further than intended. The rule reads the dependencies, devDependencies, peerDependencies and optionalDependencies of a package.json and reports a version whose range type is not allowed for its section: a pin is an exact version such as 1.2.3, a caret range starts with ^, a tilde range with ~. The workspace: protocol is understood, with workspace:* counted as a pin. A bare * is always reported. Versions the rule cannot read as a range, such as tags, URLs, file: or npm: aliases, are skipped. With no options the rule reports nothing.
Each report carries one suggestion per allowed range type that rewrites the version, such as >=1.2.3 to ^1.2.3. Suggestions appear in the JSON report and are never applied by --fix; the rule does not fix.
Reimplements eslint-plugin-package-json's restrict-dependency-ranges.
Options
The options are one restriction object, or an array of them. For each dependency the last restriction in the array whose sections include the dependency's section applies.
| Option | Type | Default | Meaning |
|---|---|---|---|
rangeType | string or string[] | required | The allowed range type or types: "pin", "caret" or "tilde". |
forDependencyTypes | string[] | all sections | The sections the restriction applies to, such as ["peerDependencies"]. |
The original's forPackages and forVersions filters are not read.
Examples
❌ Incorrect, with { "rangeType": "pin" }
{
"name": "example",
"dependencies": { "react": "^19.0.0", "lodash": "*" }
}
❌ Incorrect, with pinned dependencies and caret peers
{
"name": "example",
"peerDependencies": { "react": "19.0.0" }
}
✅ Correct, with { "rangeType": "pin" }
{
"name": "example",
"dependencies": { "react": "19.0.0", "shared": "workspace:*" },
"devDependencies": { "fixtures": "file:../fixtures" }
}
✅ Correct, with pinned dependencies and caret peers
{
"name": "example",
"dependencies": { "react": "19.0.0" },
"peerDependencies": { "react": "^19.0.0" }
}
Convention rules
The package's own rules, written for the hyperfrontend repository and general enough to ship. No preset turns them on; name each by id.
| Rule | Reads | 🔧 |
|---|---|---|
assertive-test-names | TypeScript, JavaScript | |
codeblock-line-width | Markdown | |
escape-package-tags | TypeScript, JavaScript | 🔧 |
export-order | TypeScript, JavaScript | 🔧 |
import-order | TypeScript, JavaScript | 🔧 |
jest-mock-after-imports | TypeScript, JavaScript | 🔧 |
max-file-lines | TypeScript, JavaScript | |
max-path-occurrences | TypeScript, JavaScript | |
no-ascii-art-diagrams | Markdown | |
no-async-fs-api | TypeScript, JavaScript | |
no-decorative-header-comments | TypeScript, JavaScript | 🔧 |
no-deprecated-tag | TypeScript, JavaScript | |
no-enum | TypeScript, JavaScript | |
no-inline-type-literal | TypeScript, JavaScript | |
no-manual-table-of-contents | Markdown | |
no-mixed-type-export | TypeScript, JavaScript | 🔧 |
no-mixed-type-import | TypeScript, JavaScript | 🔧 |
no-namespace-import | TypeScript, JavaScript | |
no-plain-inline-comments | TypeScript, JavaScript | 🔧 |
no-section-dividers | TypeScript, JavaScript | 🔧 |
no-todo-comments | TypeScript, JavaScript | |
no-unsafe-regex | TypeScript, JavaScript | |
one-assertion-per-test | TypeScript, JavaScript | |
prefer-exec-file-sync | TypeScript, JavaScript | |
prefer-jsdoc-over-trailing-comments | TypeScript, JavaScript | 🔧 |
readme-paragraph-length | Markdown | |
require-codeblock-language | Markdown | |
require-node-protocol | TypeScript, JavaScript | 🔧 |
assertive-test-names
Reads TypeScript, JavaScriptNo preset: turn it on by id
Forbid the word "should" in test titles.
Rule Details
A test title states a fact: "returns null for empty input" is shorter and clearer than "should return null for empty input". The rule reports a title containing the word "should", in any letter case, on the test-declaring calls of Jest-style runners: it, test, fit, xit and xtest. Modifier chains (it.only(...), test.skip(...), test.todo(...), it.failing(...), it.concurrent(...)) and table forms (it.each(table)(...), it.each`table`(...)) are checked too.
Suite titles passed to describe are left alone: they name units, not behaviours. Titles computed at run time, such as a template literal with substitutions, are not read. The rule does not fix.
Options
This rule has no options.
Examples
❌ Incorrect
it('should return the correct value', () => {})
test('should throw an error when input is invalid', () => {})
test.only('should execute the focused test', () => {})
it.each([1, 2])('should handle %s', () => {})
✅ Correct
it('returns the correct value', () => {})
test('throws an error when input is invalid', () => {})
it.each([1, 2])('handles %s', () => {})
describe('should-style prose is allowed in suite titles', () => {})
codeblock-line-width
Reads MarkdownNo preset: turn it on by id
Report documentation code lines that are wide only because they cram a list onto one line.
Rule Details
On a phone a code block shows about forty-five characters of a line, and the rest scrolls sideways. Scrolling is right for a line that has to be wide and wrong for one that only happens to be: an object literal with four members, a call with five arguments, a shell command with a run of flags. Those read better one entry per line on every screen. The rule is authoring feedback for that second kind of line, not a width limit, and it does not fix. It reports a line of a fenced code block in markdown when all three hold:
- The line is wider than
minLineLengthand no wider thanmaxLineLength, tabs counted as four columns. - Nothing about the line exempts it (see below).
- The line has a shape vertical formatting would fix: in a shell block (bash, sh, zsh, PowerShell and the like), a command with two or more flags or six or more words; in any other block, a bracketed span that opens and closes on the line and holds three or more entries.
Strings and nested brackets are skipped when entries are counted, so a comma inside a string is not an entry and f(a, g(b, c)) holds two entries, not three.
Lines the rule leaves alone
| Line | Why |
|---|---|
| Contains a URL | A URL is one token and cannot be folded |
| Contains a token of 32 or more characters with no space | A hash, a path, a key, a generated value |
Starts as a comment (//, #, *, /*, <!--, --) | Prose, not code |
Starts with import or export | Names one module however long its list is |
| A function, type, interface, class or abstract class declaration | A declaration shows a whole shape at once |
| A method or function-typed member signature | Same |
| Any line of a text, console, diff, http, mermaid, markdown or csv block | Output, data or a diagram keeps its width |
Any line wider than maxLineLength | That wide, the width is deliberate |
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
minLineLength | number | 80 | Rendered columns a line may reach before it is a candidate. |
maxLineLength | number | 140 | Rendered columns past which a line's width is taken as deliberate. |
Eighty is past what a phone or a small laptop's document column shows and is the column most editors wrap at. Lowering it toward seventy starts catching lines with a single short argument list, which is not the shape the rule is for. Past a hundred and forty, sampled documentation holds only declarations and signatures the exemptions already cover; the ceiling keeps a deliberately wide line, such as a minified sample, from being mistaken for one that grew.
Examples
❌ Incorrect
```ts
const channel = connect('link', { send, receive, provider, session: next, onDrop })
```
```bash
npm uninstall commitizen cz-conventional-changelog @commitlint/cli @commitlint/config-conventional
```
✅ Correct
```ts
const channel = connect('link', {
send,
receive,
provider,
session: next,
onDrop,
})
```
```bash
npm uninstall \
commitizen cz-conventional-changelog \
@commitlint/cli @commitlint/config-conventional
```
✅ Correct, wide by design
```bash
git clone https://github.com/example/repository.git --depth 1 --branch main --single-branch
```
```ts
function createPacket<T = unknown>(origin: string, target: string, data: Data<T>): Packet<T>
```
```text
DEBUG [config] resolving orders.json against /home/you/projects/example/config/orders.json
```
When Not To Use It
A transcript or a generated reference whose lines must keep their exact shape is better off with the rule turned off for that file. Raising minLineLength is the better lever for a package whose samples are legitimately denser than the rest. The rule pairs with require-codeblock-language, since the fence language is what tells it a block is output.
escape-package-tags
Reads TypeScript, JavaScript🔧 Fixable with --fixNo preset: turn it on by id
Require references to the configured package scopes in JSDoc descriptions to be wrapped in backticks.
Rule Details
A JSDoc description that mentions a scoped package leaves an @ at a word boundary, and every JSDoc parser, TypeDoc included, reads it as the start of a tag. The mention is either swallowed into a bogus tag or cuts off the description that follows it. The rule finds those references for the scopes listed in prefixes and its fix wraps each in backticks, which is both a valid escape and the right markup for a package name. A leading backslash escape is folded into the fix, so \@acme/logging becomes `@acme/logging`: many renderers that turn backticks into code do not process backslash escapes, and the backslash would show in the published docs.
The whole specifier is measured, scope, name and any sub-path, so @acme/core/utils is wrapped as a unit; trailing punctuation is left outside. A reference is reported when it starts a description line or follows whitespace. A description line that opens with a guarded scope is reported rather than read as a tag, since that is exactly the corruption the rule prevents.
The rule leaves alone:
- references already in backticks, or embedded in quotes such as
"@acme/core"; - everything from the first genuine block tag onwards, so
@module @acme/coreand the bodies of example tags are untouched; - fenced code blocks inside the description;
- a prefix that only starts a longer word, such as
@acmeish.
eslint-plugin-jsdoc's escape-inline-tags covers the general case but matches the tag name only, so its fix would split @acme/core into `@acme`/core. When both rules are on, hand the same scope list to that rule's allowedInlineTags option, so each occurrence has one diagnostic and one fixer.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
prefixes | string[] | [] | Package scopes, such as "@acme", that must never appear bare in a JSDoc description. |
With no prefixes the rule reports nothing.
Examples
❌ Incorrect, with { "prefixes": ["@acme"] }
/**
* End-to-end tests for @acme/versioning
*/
/**
* Entry point for the \@acme/package plugin.
*/
/**
* @acme/workspace - a plugin for generated reports.
*/
✅ Correct, with { "prefixes": ["@acme"] }
/**
* End-to-end tests for `@acme/versioning`
*/
/**
* `@acme/workspace` - a plugin for generated reports.
*/
/**
* The broker.
*
* @module @acme/nexus
*/
When Not To Use It
When a project deliberately writes package references as JSDoc tags.
export-order
Reads TypeScript, JavaScript🔧 Fixable with --fixNo preset: turn it on by id
Require re-exports to be listed type-only first, then grouped by where their source points.
Rule Details
The rule applies the grouping of import-order to re-exports, the export ... from and export * from statements of an index file. The expected order, alphabetical by source within each group:
- Type-only re-exports (
export type), themselves ordered by the groups below - Node built-in modules, with the
node:prefix or without it - External packages
- Packages in the scopes listed in
internalScopes, when any are configured - Parent-directory sources (
../), the deepest first - Current-directory sources (
./)
Exports of local declarations, such as export const or export function, are not ordered. The rule reports once, at the first re-export, when the re-exports are out of order. The fix rewrites them in the expected order, one per line, and is offered only when the re-exports form one unbroken run.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
internalScopes | string[] | [] | Package scopes, such as "@acme", whose packages form a group of their own after the external packages. |
Without internal scopes, a scoped package is an external package like any other.
Examples
❌ Incorrect
export { helper } from './helper'
export type { Config } from './types'
export { readFileSync } from 'node:fs'
export { default as express } from 'express'
❌ Incorrect, with { "internalScopes": ["@acme"] }
export * from '@acme/utils'
export * from 'express'
✅ Correct
export type { Config } from './types'
export { readFileSync } from 'node:fs'
export { default as express } from 'express'
export { helper } from './helper'
✅ Correct, with { "internalScopes": ["@acme"] }
export type * from './types'
export * from 'express'
export * from '@acme/utils'
export * from './helper'
When Not To Use It
When another tool already owns the order of re-exports with its own convention.
import-order
Reads TypeScript, JavaScript🔧 Fixable with --fixNo preset: turn it on by id
Require imports to be listed type-only first, then grouped by where their source points.
Rule Details
A predictable import order makes a file's dependencies easy to scan and keeps merge conflicts in the import block small. The rule expects this order, alphabetical by source within each group:
- Type-only imports (
import type), themselves ordered by the groups below - Node built-in modules, with the
node:prefix or without it (fs/promises) - External packages
- Packages in the scopes listed in
internalScopes, when any are configured - Parent-directory imports (
../), the deepest first - Current-directory imports (
./)
The rule reports once, at the first import, when the imports are out of that order. The fix rewrites them in the expected order, one per line, and is offered only when the imports form one unbroken run; with a statement between two imports it reports without a fix.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
internalScopes | string[] | [] | Package scopes, such as "@acme", whose packages form a group of their own after the external packages. |
Without internal scopes, a scoped package is an external package like any other.
Examples
❌ Incorrect
import { helper } from './helper'
import type { Config } from './types'
import { readFileSync } from 'node:fs'
import express from 'express'
❌ Incorrect, with { "internalScopes": ["@acme"] }
import { someUtil } from '@acme/utils'
import express from 'express'
✅ Correct
import type { Config } from './types'
import { readFileSync } from 'node:fs'
import express from 'express'
import { shared } from '../../shared'
import { core } from '../core'
import { helper } from './helper'
✅ Correct, with { "internalScopes": ["@acme"] }
import { readFileSync } from 'node:fs'
import express from 'express'
import { someUtil } from '@acme/utils'
import { helper } from './helper'
When Not To Use It
When another tool, such as a formatter plugin, already owns import ordering with its own convention. The same grouping applies to re-exports in export-order.
jest-mock-after-imports
Reads TypeScript, JavaScript🔧 Fixable with --fixNo preset: turn it on by id
Require jest.mock() and jest.unmock() calls to come after every import statement.
Rule Details
Jest's transform hoists mock registrations above the imports, and the load hook of @hyperfrontend/testing finds them wherever they sit, so placement does not change execution: the mocks register first either way. Writing them before the imports buys nothing and pushes the module's real dependencies below a wall of mock boilerplate. The rule reports each top-level jest.mock() or jest.unmock() call that comes before the last import.
The fix moves the imports above the mock calls, keeping each group's order. It is offered only when the imports and mocks form one unbroken run; with another statement among them the rule reports without a fix.
Options
This rule has no options.
Examples
❌ Incorrect
jest.mock('./warn-handler', () => ({ createHandler: jest.fn() }))
jest.mock('@rollup/plugin-json', () => jest.fn(() => ({ name: 'json' })))
import type { BuildContext } from '../../models'
import { createConfig } from './config'
import { createHandler } from './warn-handler'
✅ Correct
import type { BuildContext } from '../../models'
import { createConfig } from './config'
import { createHandler } from './warn-handler'
jest.mock('./warn-handler', () => ({ createHandler: jest.fn() }))
jest.mock('@rollup/plugin-json', () => jest.fn(() => ({ name: 'json' })))
Limitations
Only calls on an object named jest, written as top-level statements, are read; a mock registered through another name or inside a block is not seen.
max-file-lines
Reads TypeScript, JavaScriptNo preset: turn it on by id
Limit the line count of files that hold more than one function.
Rule Details
A long file with many functions usually carries more than one responsibility, and splitting it gives smaller modules that are easier to read, test and review. The rule counts the functions in a file (declarations, function expressions, arrow functions and class methods, inline callbacks included) and, when there are two or more, reports a file longer than its budget. A file with one function or none may be as long as it needs. The rule does not fix.
Test files, whose names end in .spec or .test followed by .ts, .tsx, .js or .jsx, have their own, higher budget.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
maxLines | number | 300 | The most lines an implementation file may hold. |
maxLinesTest | number | 500 | The most lines a test file may hold. |
Examples
❌ Incorrect, with { "maxLines": 4 }
export function parse(input: string): string[] {
return input.split(',')
}
export function validate(items: string[]): boolean {
return items.every((item) => item.length > 0)
}
✅ Correct, with { "maxLines": 4 }, one function of any length
export function parse(input: string): string[] {
const items = input.split(',')
const kept = items.filter(Boolean)
return kept
}
When Not To Use It
For generated files, or for files that are comprehensive by design, such as a module of type definitions. A per-file override is usually better than turning the rule off. Unlike ESLint's max-lines, this rule exempts single-function files.
Limitations
Lines are counted by line starts, so a file that ends with a newline counts the empty line after it: a 300-line file with a final newline counts as 301.
max-path-occurrences
Reads TypeScript, JavaScriptNo preset: turn it on by id
Allow at most one type import and one value import per module path, and in an index file one type re-export and one value re-export per path.
Rule Details
Imports from the same module scattered over several statements are noise that belongs in one statement. The rule groups a file's import statements by source and kind, import type apart from value imports, and reports every statement of a group that holds more than one. The pattern it leaves room for is one type import and one value import per path.
Re-exports (export ... from and export * from) are grouped the same way, but only in index files (index.ts, index.tsx, index.js, index.jsx), where they make up the public surface. Other files may re-export from one path as often as they like. The rule does not fix.
Options
This rule has no options.
Examples
❌ Incorrect
import { foo } from './module'
import { bar } from './module'
import type { User } from './types'
import type { Config } from './types'
✅ Correct
import type { User, Config } from './types'
import { createUser } from './types'
Re-exports outside an index file are not counted:
export { foo } from './module'
export { bar } from './module'
Limitations
An inline type specifier (import { type User } from './types') makes a value import, so it shares a group with the other value imports from the path. Pair the rule with no-mixed-type-import to keep the two kinds apart.
no-ascii-art-diagrams
Reads MarkdownNo preset: turn it on by id
Forbid diagrams drawn with box characters in markdown code blocks; prefer Mermaid.
Rule Details
A diagram drawn in box characters is hard to edit, unreadable to a screen reader, and breaks at any width or font other than the author's. A Mermaid diagram is source a reader can change and a renderer can lay out. The rule reports a fenced code block whose lines draw a box: at least three lines carrying two or more box characters (┌ ┐ └ ┘ │ ─ ├ ┤ ┬ ┴ ┼, their double-line forms, + and |), with an opening corner, a right-hand border character, and at least two lines with a vertical border. The rule does not fix.
A directory tree drawn with ├── and └── is not a box and is left alone, as are markdown tables outside code blocks. In a file named README.md the message points at a separate architecture document instead, since npm does not render Mermaid.
Options
This rule has no options.
Examples
❌ Incorrect
The pipeline:
```
┌──────────────────────────────────────┐
│ PIPELINE │
├──────────────────────────────────────┤
│ Input ──▶ │ Process │ ──▶ │ Output │
└──────────────────────────────────────┘
```
✅ Correct
The pipeline:
<div data-mermaid-id="mermaid-block-0"></div>
✅ Correct, a directory tree
The project layout:
```
src/
├── index.ts
├── utils/
│ └── helper.ts
└── types.ts
```
Limitations
Only fences of backticks are read; a block fenced with tildes is not checked.
no-async-fs-api
Reads TypeScript, JavaScriptNo preset: turn it on by id
Forbid the asynchronous Node file system APIs in favour of their synchronous twins.
Rule Details
In scripts, build tools and command-line programs, synchronous file access is simpler to read and to order: each operation finishes before the next line runs, and errors arrive as ordinary exceptions. The rule reports:
- an import or
require()offs/promisesornode:fs/promises; - a named import from
fsornode:fsof a method that has a synchronous twin, such asreadFile(usereadFileSync); - such a method read through a namespace or default import of
fs, such asfs.readFile(...).
The set of methods is read from the running Node's own fs module: every method that has a twin of the same name ending in Sync. The rule does not fix.
Options
This rule has no options.
Examples
❌ Incorrect
import { readFile } from 'node:fs/promises'
const content = await readFile('file.txt', 'utf-8')
import * as fs from 'node:fs'
fs.readFile('file.txt', (error, data) => {})
✅ Correct
import { readFileSync, writeFileSync } from 'node:fs'
const content = readFileSync('file.txt', 'utf-8')
writeFileSync('output.txt', content)
When Not To Use It
In servers and other long-running processes, where blocking I/O stalls the event loop, and wherever many files are processed concurrently.
Limitations
The promise API reached as a property, fs.promises.readFile(...), and methods called on an inline require('fs') are not reported.
no-decorative-header-comments
Reads TypeScript, JavaScript🔧 Fixable with --fixNo preset: turn it on by id
Forbid a file from opening with a block comment of prose alone that documents nothing.
Rule Details
A header such as "File: utils.ts, Description: utility functions" repeats what the file name and the code already say, and goes stale. The rule reads the first comment of a file, when nothing but whitespace precedes it, and reports it when it is a block comment of two or more lines that:
- carries no tag (no line opens with
@, so@module,@packageDocumentationand tooling directives such as@vitest-environment jsdomare kept), and - documents no declaration: a blank line separates it from the code, nothing follows it, or the code that follows does not open with a declaration keyword such as
export,function,constorclass.
A one-line block comment and a line comment are left alone. The fix removes the comment and the whitespace after it.
Options
This rule has no options.
Examples
❌ Incorrect
/**
* This is the vfs module.
* It provides virtual file system utilities.
*/
export function createVfs() {}
/*
* File: utils.ts
* Description: Utility functions for the application
*/
describe('utils', () => {})
✅ Correct
/**
* Virtual file system utilities.
*
* @module vfs
*/
export function createVfs() {}
/**
* Creates an empty virtual file system.
* Paths are POSIX.
*/
export function createVfs() {}
/**
* @vitest-environment jsdom
*/
describe('DOM tests', () => {})
no-deprecated-tag
Reads TypeScript, JavaScriptNo preset: turn it on by id
Forbid the @deprecated tag in block comments.
Rule Details
The rule is for projects that ship breaking changes in a major version and remove the code they replace, rather than keeping it beside a deprecation warning. Deprecated code that stays accumulates, and a tag that says "use the other one" leaves two ways to do one thing. The rule reports the first @deprecated tag of every block comment, in any letter case, when the word is followed by whitespace, * or the end of the comment. A mention inside backticks or another word, such as `@deprecated` in prose, passes. The rule does not fix.
Options
This rule has no options.
Examples
❌ Incorrect
/**
* @deprecated Use newFunction instead
*/
export function oldFunction(): void {}
✅ Correct
/**
* Performs the operation. Replaces the previous implementation, removed in
* the last major version.
*/
export function newFunction(): void {}
When Not To Use It
When a project keeps backwards compatibility across releases, runs long deprecation cycles, or publishes APIs whose users should be warned before something is removed.
no-enum
Reads TypeScript, JavaScriptNo preset: turn it on by id
Forbid TypeScript enum declarations in favour of frozen const objects.
Rule Details
An enum compiles to a function call that bundlers cannot tree-shake, and it is one of the few TypeScript constructs with run-time behaviour of its own, such as the reverse mapping of a numeric enum. A frozen const object with <const> says the same thing, stays plain JavaScript, and a type derived from it gives the union of its values. The rule reports every enum declaration, const enum and declare enum included. It does not fix; its message shows the replacement to write.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
freezeImport | string | none | An import statement the replacement in the message starts with, such as "import { freeze } from 'my-utils'"; the replacement then calls the imported function. |
Without it the message suggests Object.freeze(...).
Examples
❌ Incorrect
enum Status {
Active = 'active',
Inactive = 'inactive',
}
enum Direction {
Up,
Down,
}
✅ Correct
const Status = Object.freeze(<const>{
Active: 'active',
Inactive: 'inactive',
})
type Status = (typeof Status)[keyof typeof Status]
When Not To Use It
When a project prefers enums, or relies on enum-only features such as the reverse mapping of numeric members.
no-inline-type-literal
Reads TypeScript, JavaScriptNo preset: turn it on by id
Forbid anonymous object types with named members at use sites; declare the shape as a named type alias or interface.
Rule Details
An object type written inline, such as { a: number; b: string } in a parameter annotation, is not a declaration, so it cannot carry JSDoc, cannot be imported or extended, and does not show up in an editor's symbol search. A change to the shape has to be repeated wherever the literal was copied. The rule reports every object type literal with at least one property or method member, wherever it sits: parameter and return annotations, variable and class property annotations, type assertions and satisfies, type arguments, unions and intersections, generic constraints, and literals nested inside a type alias. The rule does not fix.
Exempt are the literal that is the whole right-hand side of a type alias (the place a shape is named), an interface body, the empty type {}, and literals holding only index signatures ({ [key: string]: T }) or call signatures ({ (): void }), which have no named members to document.
Options
This rule has no options.
Examples
❌ Incorrect
function start({ logger }: { logger: Logger }): void {}
function check(): { ok: boolean } {
return { ok: true }
}
const m: Map<string, { a: number }> = new Map()
type Union = string | { a: number }
type Outer = { inner: { a: number } }
✅ Correct
type StartOptions = { logger: Logger }
function start({ logger }: StartOptions): void {}
interface CheckResult {
ok: boolean
}
function check(): CheckResult {
return { ok: true }
}
type Inner = { a: number }
type Outer = { inner: Inner }
const empty: {} = {}
const counts: { [key: string]: number } = {}
no-manual-table-of-contents
Reads MarkdownNo preset: turn it on by id
Forbid hand-written table-of-contents sections in markdown.
Rule Details
Documentation sites and repository hosts draw an on-page index from a document's headings. A table of contents written by hand says the same thing a second time, goes stale as soon as a heading changes, and takes the first screen of the page away from the content. The rule reports a heading, at any level, whose whole text names an index once letter case, repeated spaces and trailing punctuation are set aside. The remedy is to remove the heading and the list under it. The rule does not fix.
The headings reported are: Table of Contents, Table of Content, Contents, TOC, Index, Index of Contents, Document Index, Page Index, Section Index, Quick Index, Full Index, Document Contents, Page Contents, Section Contents, Quick Contents, In this document, In this page, In this section and In this guide.
Only headings are read, so the word "index" in a sentence, a list item or a code block is never reported, and a heading about something else, such as "Index Signatures" or "Indexing the cache", carries words that do not name a table of contents.
Options
This rule has no options.
Examples
❌ Incorrect
# Architecture
### Table of Contents
1. [Overview](#overview)
2. [Design](#design)
### Overview
✅ Correct
# Architecture
### Overview
### Index Signatures
A mapped type with an index signature.
When Not To Use It
When the documents are read somewhere that draws no index from the headings, such as a plain-text viewer or a printed handout.
no-mixed-type-export
Reads TypeScript, JavaScript🔧 Fixable with --fixNo preset: turn it on by id
Forbid an export list that mixes inline type specifiers with value specifiers.
Rule Details
An export list that names types and values together hides which names survive compilation: the types are erased, the values stay. Keeping them in an export type statement and a plain export makes the split visible and gives tools that elide types one statement to drop. The rule reports an export list, a re-export or a local one, that holds both specifiers marked type and unmarked ones. The fix splits it into an export type { … } and an export { … } of the same source, keeping each specifier's alias.
A list whose specifiers are all marked type, or none of them, is not reported.
Options
This rule has no options.
Examples
❌ Incorrect
export { type User, createUser } from './module'
export { type Config, type Options, initConfig as init } from './config'
✅ Correct
export type { User } from './module'
export { createUser } from './module'
export type { Config, Options } from './config'
export { initConfig as init } from './config'
When Not To Use It
When a project prefers the brevity of inline type specifiers. The same split for imports is no-mixed-type-import.
no-mixed-type-import
Reads TypeScript, JavaScript🔧 Fixable with --fixNo preset: turn it on by id
Forbid an import that mixes inline type specifiers with value specifiers.
Rule Details
An import that names types and values together hides which names the compiled module still needs: the types are erased, the values stay. Keeping them in an import type statement and a plain import makes the split visible and gives tools that elide types one statement to drop. The rule reports an import whose named specifiers include both ones marked type and unmarked ones. The fix splits it into an import type { … } and a plain import of the same source; a default import stays with the values, and each specifier keeps its alias.
An import whose named specifiers are all marked type, or none of them, is not reported.
Options
This rule has no options.
Examples
❌ Incorrect
import { Component, type ComponentProps, useState } from 'react'
import { helper, type HelperOptions as Options } from './utils'
✅ Correct
import type { ComponentProps } from 'react'
import { Component, useState } from 'react'
import type { HelperOptions as Options } from './utils'
import { helper } from './utils'
When Not To Use It
When a project prefers the brevity of inline type specifiers. The same split for exports is no-mixed-type-export.
Limitations
A default import beside named specifiers that are all marked type, as in import React, { type FC } from 'react', is not reported, since no named value specifier is present.
no-namespace-import
Reads TypeScript, JavaScriptNo preset: turn it on by id
Forbid namespace imports (import * as name) in favour of named imports.
Rule Details
A namespace import hides which of a module's exports a file uses, and some bundlers keep every export of a module imported that way. Named imports say what is used and let unused exports be dropped. The rule reports each namespace import specifier. The rule does not fix.
Two kinds of namespace import are allowed: a type-only one (import type * as Types from './types'), which is erased at compile time, and one whose source ends in .json, since a JSON module has no named exports to pick.
Options
This rule has no options.
Examples
❌ Incorrect
import * as utils from './utils'
utils.helper()
✅ Correct
import { helper } from './utils'
import type * as Types from './types'
import * as config from './config.json'
helper()
When Not To Use It
For modules designed to be used as a namespace, or where a file deliberately groups a module's exports under one name.
Limitations
Namespace re-exports (export * as utils from './utils') are not reported.
no-plain-inline-comments
Reads TypeScript, JavaScript🔧 Fixable with --fixNo preset: turn it on by id
Require every line comment to open with a hint prefix such as why: or to be a tooling directive.
Rule Details
A comment that restates the next line is noise, and one that explains a decision is easy to lose among them. The rule asks each // comment to say what kind of note it is, by opening with one of the hint prefixes below (in any letter case), or to be a directive some tool reads. Any other line comment is reported. Block comments are not read.
| Prefix | Purpose |
|---|---|
why: | The rationale behind a decision that is not obvious |
how: | An implementation approach that is not obvious |
context: | Background or an outside constraint |
magic: | The origin or meaning of a hard-coded value |
todo: | Known work to be done |
fixme: | A known defect |
note: | A callout that fits no other kind |
ref: | A link to an outside resource, ticket or specification |
compat: | A shim kept only for a runtime below a version bound |
A compat: comment names the runtime and the first version at which the shim can go, then why it exists, as in compat: node<22 the WebSocket global arrived in Node 22; searching for compat: node< then lists every shim a raised Node floor would retire.
The directives recognised are TypeScript's (@ts-ignore, @ts-expect-error, @ts-nocheck, @ts-check, triple-slash references), the linter's own (lint-disable, lint-enable and their line forms) and eslint-env, coverage hints (istanbul ignore, c8 ignore), bundler hints (webpack's magic comments, @vite-ignore, @rollup-plugin-ignore), prettier-ignore, //# sourceMappingURL= and //# sourceURL=, fall-through markers (// falls through, // fallthrough, // no default), and <hf:…> region markers.
A comment that fills an otherwise empty function or catch body is left alone, and configuration files are skipped altogether: any file whose name contains a configuration name such as tsconfig*.json, lint.config.*, eslint.config.*, vite.config.*, jest.config.*, webpack.config.*, rollup.config.*, prettier.config.* or package.json.
The fix deletes the comment: its whole line when it stands alone, or the comment and the space before it when it trails code. Review the result, since a plain comment that held something worth keeping is better rewritten with a prefix.
Options
This rule has no options.
Examples
❌ Incorrect
// Initialize the counter
let counter = 0
const x = 1 // increment later
✅ Correct
// why: the API's indexes start at 0
let counter = 0
// magic: 86400 = seconds in a day (60 * 60 * 24)
const SECONDS_PER_DAY = 86400
// ref: https://github.com/org/repo/issues/123
function workaround() {}
try {
workaround()
} catch {
// ignored on purpose
}
// lint-disable-next-line no-console
console.log('Debug')
When Not To Use It
When a team prefers unrestricted commenting. A project that writes many comments spanning several lines should know that each continuation line is a comment of its own and must carry a prefix too.
no-section-dividers
Reads TypeScript, JavaScript🔧 Fixable with --fixNo preset: turn it on by id
Forbid section divider comments drawn with four or more equals signs.
Rule Details
A banner such as // ==== Helpers ==== splits a file visually but not structurally, and a file long enough to need one usually wants separate modules, classes or functions instead. The rule reports every line comment containing ====. When the divider opens a run of consecutive line comments that closes with another divider, as in a banner of three lines, the run is reported as one. The fix removes the divider, or the whole banner, together with the line break that ends it.
Options
This rule has no options.
Examples
❌ Incorrect
// ============================
// User Management
// ============================
function createUser() {}
// ==== Helpers ====
function helper() {}
✅ Correct
// note: user management
function createUser() {}
function helper() {}
no-todo-comments
Reads TypeScript, JavaScriptNo preset: turn it on by id
Forbid TODO markers, in any spelling, in comments.
Rule Details
Work left for later belongs in an issue tracker, where it has an owner and stays visible; a TODO in the code goes stale unseen. The rule reports every occurrence of the marker in line, block and JSDoc comments, in any letter case and spelled as one word, hyphenated or spaced (TODO, to-do, to do), the @todo tag included. The marker must stand alone: a letter, digit, underscore or hyphen next to it makes it part of a longer word, so path-to-document, auto download and todo-app pass. The rule does not fix.
Options
This rule has no options.
Examples
❌ Incorrect
// TODO: fix validation
/* TO-DO: refactor this */
/** @todo add error handling */
✅ Correct
// note: validates the input format
// see path-to-document for details
// prevents an auto download of files
// the todo-app package handles this
When Not To Use It
When TODO comments are an accepted part of a project's workflow. The todo: prefix that no-plain-inline-comments allows is reported here, so a project that turns both rules on gives up that prefix.
Limitations
The spaced spelling is matched in ordinary prose too: a comment such as "the steps to do first" is reported.
no-unsafe-regex
Reads TypeScript, JavaScriptNo preset: turn it on by id
Forbid regular expressions that risk catastrophic backtracking, and expressions built from values the code does not spell out.
Rule Details
A pattern with a repetition nested inside another, such as /^(a+)+$/, can take exponential time on a crafted input, which is a denial-of-service hole (ReDoS) wherever the input is untrusted. The rule reads every regular expression literal and every RegExp(...), new RegExp(...) and createRegExp(...) call whose pattern is a string or a template without substitutions, and reports a pattern that:
- holds a repetition inside another repetition, judged by star height the way the safe-regex package judges it,
?counted as a repetition; - holds more than 25 repetitions in all;
- names a quantifier upper bound past 1000, as in
{1,100000}; - cannot be read, such as one with a lookbehind.
A RegExp or createRegExp call whose pattern is computed (a variable, a call, a template with substitutions) is reported too, since the pattern may come from input. The rule does not fix.
Options
This rule has no options. The thresholds are fixed: a bound of 1000 and 25 repetitions. Settings that similar rules offer, such as configurable thresholds, ignored patterns, extra factory functions or turning off the dynamic-construction check, are not supported.
Examples
❌ Incorrect
const nested = /^(a+)+$/
const starred = /([a-zA-Z]+)*$/
const bounded = /a{1,100000}/
const dynamic = new RegExp(userInput)
const templated = new RegExp(`^${prefix}`)
✅ Correct
const flat = /^a+$/
const word = /[a-zA-Z]+$/
const bounded = /a{1,100}/
const constant = new RegExp('^[a-z]+$', 'i')
Limitations
Like safe-regex, the check does not look inside a repeated group with alternatives, so overlapping alternatives such as /(a|a)+/ pass, and an exact bound such as {5000} is not measured.
See OWASP on ReDoS for background.
one-assertion-per-test
Reads TypeScript, JavaScriptNo preset: turn it on by id
Limit each test body to one assertion, counting the assertions reached through same-file helpers.
Rule Details
A unit test describes one behaviour. A test with two assertions names two behaviours at once, stops at the first failure and hides the second, and needs a title vague enough to cover both. The rule reports every test whose body reaches more than one assertion, on the declaring call, and names where the assertions are, so the author can choose between two remedies:
- Split the test into focused tests, each titled for the single behaviour it asserts.
- Fold the checks into one assertion when they jointly describe one indivisible behaviour, with a composite or asymmetric matcher such as
toEqual(expect.objectContaining({ ... }))ortoMatchObject(...). A fold should still name what failed: compare a keyed object ({ before, after }) rather than a positional array of booleans whose failure reads[true, false].
Which remedy fits is a judgement about the behaviour under test, so the rule does not fix. It never requires an assertion: a test with none is left alone.
What is read
Tests are calls such as it(...), test(...), fit(...), xit(...) and xtest(...), modifier chains such as it.only(...), test.skip(...), it.todo(...), it.failing(...) and it.concurrent(...) included, and the table forms such as it.each(table)(...). The body is the first argument that is a function, or a name bound to a function in the same file. Suites and hooks are not tests, so assertions in them are not counted.
Assertions are expect(...) (under any local alias), expect.soft(...) and expect.poll(...), and every call into the assert module, imported from node:assert, node:assert/strict, assert or assert/strict as a default, a namespace or named functions. Matcher builders (expect.any(...), expect.objectContaining(...), expect.not.* and the like) and the count declarations expect.assertions(n) and expect.hasAssertions() are not assertions.
How assertions are counted
The count is lexical: every assertion written in the body counts once, including those inside nested callbacks such as an items.forEach(...) callback or a mock implementation. A loop that asserts once per element is one assertion over a collection; an if/else asserting in both branches is two. A reference to a function declared in the same file, called or passed as a callback, adds every assertion that function reaches, through further helpers too, so a helper called twice counts twice. A test declared inside another test is reported on its own and adds nothing to its parent.
Options
This rule has no options. To leave files alone, such as fixture suites whose assertion shape is itself under test, scope the rule with the configuration's files and ignores.
Examples
❌ Incorrect
it('parses the manifest', () => {
const manifest = parse(source)
expect(manifest.name).toBe('demo')
expect(manifest.version).toBe('1.2.3')
})
function expectValid(result: Result): void {
expect(result.ok).toBe(true)
expect(result.errors).toEqual([])
}
it('validates the manifest', () => {
expectValid(validate(manifest))
})
✅ Correct
it('parses the manifest identity', () => {
const manifest = parse(source)
expect(manifest).toEqual(expect.objectContaining({ name: 'demo' }))
})
it('validates the manifest', () => {
expect(validate(manifest)).toEqual({ ok: true, errors: [] })
})
it('reports every item as defined', () => {
items.forEach((item) => expect(item).toBeDefined())
})
it('declares its assertion count', () => {
expect.assertions(1)
expect(add(1, 2)).toBe(3)
})
When Not To Use It
For suites written as integration scenarios that walk through many states in one test, where a split would break the scenario. Scope the rule away from those files rather than switching it off for a project.
Limitations
The rule reads one file at a time and has no type information. An assertion helper imported from another module, a method called on a class instance, and a body built by a call (it('x', makeBody())) are not seen.
prefer-exec-file-sync
Reads TypeScript, JavaScriptNo preset: turn it on by id
Forbid execSync from the child process module in favour of execFileSync.
Rule Details
execSync runs its command string through a shell. Anything interpolated into the string is parsed by that shell, so a path with a space breaks the command and untrusted input can inject one; quoting rules also differ between bash, cmd and PowerShell. execFileSync runs the executable directly with an argument array that no shell sees. The rule reports execSync wherever it is taken from child_process or node:child_process:
- a named import, or a property destructured from
require()of the module; - a member read through a namespace, default import or required module object, dotted or bracketed (
cp.execSync(...),cp['execSync'](...)).
The rule does not fix: moving to execFileSync means splitting the command into the executable and its arguments, and replacing shell features with Node code.
Options
This rule has no options.
Examples
❌ Incorrect
import { execSync } from 'node:child_process'
execSync(`git add ${filePath}`)
import * as cp from 'node:child_process'
cp.execSync('npm install')
const { execSync } = require('node:child_process')
✅ Correct
import { execFileSync } from 'node:child_process'
execFileSync('git', ['add', filePath])
execFileSync('git', ['commit', '-m', 'a message with spaces'])
When Not To Use It
When a script needs shell features such as pipes, redirects or globbing on purpose. Node APIs usually cover those without a shell.
Limitations
Only the synchronous function is covered: exec and the shell: true option of the other functions also run through a shell and are not reported.
prefer-jsdoc-over-trailing-comments
Reads TypeScript, JavaScript🔧 Fixable with --fixNo preset: turn it on by id
Require interface members to be documented by a JSDoc block above them, not by a trailing line comment.
Rule Details
Documentation tools such as TypeDoc and editors' hover cards collect a member's JSDoc block and ignore a // comment trailing it on the same line, so the description is lost wherever the member is read. The rule reports a line comment that follows an interface member on the member's own line. The fix moves the comment's text into a /** … */ block above the member, at the member's indentation. When the member already has a JSDoc block above it, the rule reports without a fix, so existing documentation is never overwritten.
Options
This rule has no options.
Examples
❌ Incorrect
interface User {
id: string // unique identifier
name: string // display name
}
✅ Correct
interface User {
/** Unique identifier */
id: string
/** Display name */
name: string
}
Limitations
Only interface bodies are read. Members of a type literal, a class or an object literal with a trailing comment are not reported.
readme-paragraph-length
Reads MarkdownNo preset: turn it on by id
Report markdown paragraphs that have grown into walls of text.
Rule Details
A paragraph is the unit a reader commits to before a break. Past a certain length the eye loses its place returning to the margin, the reader cannot tell which sentence carries the point, and a summariser has no boundary to summarise around. The remedy is rarely a break at the midpoint: it is one idea per paragraph, or fewer words. The rule reports a paragraph longer than maxCharacters. It does not fix.
The rule reads the markdown tree, so code blocks, tables, headings, HTML blocks and link definitions are never mistaken for prose. It measures rendered characters, not source characters:
| Node | Contributes |
|---|---|
| text and inline code | its text |
| link, link reference | the link text only, never the URL |
| emphasis, strong, delete | the text inside |
| image, image reference | nothing |
| inline HTML, hard line break | nothing |
Runs of whitespace collapse to one space before the count, so a paragraph hard-wrapped over ten source lines measures the same as one written on a single line. A paragraph inside a list item, a blockquote, a table cell or a footnote definition is exempt: those are fragments whose length the surrounding structure governs.
Options
| Option | Type | Default | Meaning |
|---|---|---|---|
maxCharacters | number | 700 | Rendered characters a paragraph may reach before it is reported. |
Seven hundred is about six lines at a typical documentation column of 115 characters, and sits above the 95th percentile of paragraph length in a sample of nearly a thousand library README paragraphs, so the rule flags genuine walls of text and stays silent on ordinary technical writing.
Examples
❌ Incorrect, with { "maxCharacters": 200 }
The host owns the geometry and the feature never measures itself, which
means the contract declares every display mode up front, and the host picks
one at open time and passes the measured size across, and because the
feature cannot ask for a different size its only lever is to refuse a mode.
✅ Correct, with { "maxCharacters": 200 }
The host owns the geometry: a feature never measures itself. The contract
declares every display mode, and the host picks one at open time.
A feature's only lever is refusal: it can decline a mode it cannot render.
- A list item is a fragment, so its length is left to the list around it,
however many words it takes to describe the step it stands for in full.
When Not To Use It
For generated documents, or transcripts that must keep their original paragraphing. Raising maxCharacters is the better lever when a whole package's documentation is legitimately denser than the rest.
require-codeblock-language
Reads MarkdownNo preset: turn it on by id
Require a language on every fenced code block in markdown.
Rule Details
The language after the opening fence is what lets a renderer highlight the block, a screen reader announce it, and tools such as codeblock-line-width tell code from output. A reader also knows at a glance what they are looking at. The rule reports an opening fence of backticks with nothing after it. For output or plain text, use the language text or console. The rule does not fix.
Options
This rule has no options.
Examples
❌ Incorrect
```
const x = 1
```
✅ Correct
```ts
const x = 1
```
```json
{ "name": "example" }
```
```text
This is plain text output
```
Limitations
Fences are paired line by line without regard to their length, so a block fenced with four backticks that shows a three-backtick fence inside it is not read the way a markdown renderer reads it. Fences of tildes are not checked.
require-node-protocol
Reads TypeScript, JavaScript🔧 Fixable with --fixNo preset: turn it on by id
Require the node: prefix on imports of Node built-in modules.
Rule Details
The node: prefix names a built-in unambiguously: a reader tells it from an npm package at a glance, and a package published under the same name can never shadow it. The rule reports an import whose source is a Node built-in written without the prefix, judged by its first path segment against the running Node's list of built-ins, so fs/promises counts as well as fs. The fix adds the prefix, keeping the quote style.
Options
This rule has no options.
Examples
❌ Incorrect
import { readFileSync } from 'fs'
import path from 'path'
import { setTimeout } from 'timers/promises'
✅ Correct
import { readFileSync } from 'node:fs'
import path from 'node:path'
import { setTimeout } from 'node:timers/promises'
import express from 'express'
When Not To Use It
When the code must run on a Node too old for the prefix (before 14.18 for require(), before 12.20 for ES modules).
Limitations
Only import declarations are read. require() calls, dynamic import() expressions and re-exports (export * from 'fs') are not reported.