eslint-config-ts-prefixer
A zero-config TypeScript ESLint configuration with Prettier integration
Installation
Install the package using your preferred package manager:
pnpm add -D eslint-config-ts-prefixer@latestnpm install --save-dev eslint-config-ts-prefixer@latestyarn add -D eslint-config-ts-prefixerConfiguration
Add to your eslint.config.js:
import { defineConfig } from 'eslint/config'
import tsPrefixer from 'eslint-config-ts-prefixer'
export default defineConfig([...tsPrefixer])Add lint scripts to your package.json:
{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint . --fix"
  }
}Configured Rules
Below is a comprehensive list of all ESLint rules configured by this package:
The reason for this is that == and != do type coercion which follows the rather obscure Abstract Equality Comparison Algorithm.
For instance, the following statements are all considered true:
- [] == false
- [] == ![]
- 3 == "03"
If one of those occurs in an innocent-looking statement such as a == b the actual problem is very difficult to spot.
Rule Details
This rule is aimed at eliminating the type-unsafe equality operators.
Examples of incorrect code for this rule:
/*eslint eqeqeq: "error"*/
if (x == 42) {
}
if ('' == text) {
}
if (obj.getStuff() != undefined) {
}
The --fix option on the command line automatically fixes some problems reported by this rule. A problem is only fixed if one of the operands is a typeof expression, or if both operands are literals with the same type.
Options
always
The "always" option (default) enforces the use of === and !== in every situation (except when you opt-in to more specific handling of null [see below]).
Examples of incorrect code for the "always" option:
/*eslint eqeqeq: ["error", "always"]*/
a == b
foo == true
bananas != 1
value == undefined
typeof foo == 'undefined'
'hello' != 'world'
0 == 0
true == true
foo == null
Examples of correct code for the "always" option:
/*eslint eqeqeq: ["error", "always"]*/
a === b
foo === true
bananas !== 1
value === undefined
typeof foo === 'undefined'
'hello' !== 'world'
0 === 0
true === true
foo === null
This rule optionally takes a second argument, which should be an object with the following supported properties:
- "null": Customize how this rule treats- nullliterals. Possible values:- always(default) - Always use- ===or- !==.
- never- Never use- ===or- !==with- null.
- ignore- Do not apply this rule to- null.
 
smart
The "smart" option enforces the use of === and !== except for these cases:
- Comparing two literal values.
- Evaluating the value of typeof.
- Comparing against null.
Examples of incorrect code for the "smart" option:
/*eslint eqeqeq: ["error", "smart"]*/
// comparing two variables requires ===
a == b
// only one side is a literal
foo == true
bananas != 1
// comparing to undefined requires ===
value == undefined
Examples of correct code for the "smart" option:
/*eslint eqeqeq: ["error", "smart"]*/
typeof foo == 'undefined'
'hello' != 'world'
0 == 0
true == true
foo == null
allow-null
Deprecated: Instead of using this option use "always" and pass a "null" option property with value "ignore". This will tell ESLint to always enforce strict equality except when comparing with the null literal.
;['error', 'always', { null: 'ignore' }]
When Not To Use It
If you don't want to enforce a style for using equality operators, then it's safe to disable this rule.
These errors are especially common in complex expressions where operator precedence is easy to misjudge. For example:
// One might think this would evaluate as `a + (b ?? c)`:
const x = a + b ?? c
// But it actually evaluates as `(a + b) ?? c`. Since `a + b` can never be null,
// the `?? c` has no effect.
Additionally, this rule detects comparisons to newly constructed objects/arrays/functions/etc. In JavaScript, where objects are compared by reference, a newly constructed object can never === any other value. This can be surprising for programmers coming from languages where objects are compared by value.
// Programmers coming from a language where objects are compared by value might expect this to work:
const isEmpty = x === []
// However, this will always result in `isEmpty` being `false`.
Rule Details
This rule identifies == and === comparisons which, based on the semantics of the JavaScript language, will always evaluate to true or false.
It also identifies ||, && and ?? logical expressions which will either always or never short-circuit.
Examples of incorrect code for this rule:
/*eslint no-constant-binary-expression: "error"*/
const value1 = +x == null
const value2 = condition ? x : {} || DEFAULT
const value3 = !foo == null
const value4 = new Boolean(foo) === true
const objIsEmpty = someObj === {}
const arrIsEmpty = someArr === []
const shortCircuit1 = condition1 && false && condition2
const shortCircuit2 = condition1 || true || condition2
const shortCircuit3 = condition1 ?? 'non-nullish' ?? condition2
Examples of correct code for this rule:
/*eslint no-constant-binary-expression: "error"*/
const value1 = x == null
const value2 = (condition ? x : {}) || DEFAULT
const value3 = !(foo == null)
const value4 = Boolean(foo) === true
const objIsEmpty = Object.keys(someObj).length === 0
const arrIsEmpty = someArr.length === 0
if (false) {
  doSomethingUnfinished()
}
Rule Details
This rule disallows constant expressions in the test condition of:
- if,- for,- while, or- do...whilestatement
- ?:ternary expression
Examples of incorrect code for this rule:
/*eslint no-constant-condition: "error"*/
if (false) {
  doSomethingUnfinished()
}
if (void x) {
  doSomethingUnfinished()
}
if ((x &&= false)) {
  doSomethingNever()
}
if (class {}) {
  doSomethingAlways()
}
if (new Boolean(x)) {
  doSomethingAlways()
}
if (Boolean(1)) {
  doSomethingAlways()
}
if (undefined) {
  doSomethingUnfinished()
}
if ((x ||= true)) {
  doSomethingAlways()
}
for (; -2; ) {
  doSomethingForever()
}
while (typeof x) {
  doSomethingForever()
}
do {
  doSomethingForever()
} while ((x = -1))
const result = 0 ? a : b
if (input === 'hello' || 'bye') {
  output(input)
}
Examples of correct code for this rule:
/*eslint no-constant-condition: "error"*/
if (x === 0) {
  doSomething()
}
for (;;) {
  doSomethingForever()
}
while (typeof x === 'undefined') {
  doSomething()
}
do {
  doSomething()
} while (x)
const result = x !== 0 ? a : b
if (input === 'hello' || input === 'bye') {
  output(input)
}
Options
checkLoops
This is a string option having following values:
- "all"- Disallow constant expressions in all loops.
- "allExceptWhileTrue"(default) - Disallow constant expressions in all loops except- whileloops with expression- true.
- "none"- Allow constant expressions in loops.
Or instead you can set the checkLoops value to booleans where true is same as "all" and false is same as "none".
Examples of incorrect code for when checkLoops is "all" or true:
/*eslint no-constant-condition: ["error", { "checkLoops": "all" }]*/
while (true) {
  doSomething()
}
for (; true; ) {
  doSomething()
}
/*eslint no-constant-condition: ["error", { "checkLoops": true }]*/
while (true) {
  doSomething()
}
do {
  doSomething()
} while (true)
Examples of correct code for when checkLoops is "all" or true:
/*eslint no-constant-condition: ["error", { "checkLoops": "all" }]*/
while (a === b) {
  doSomething()
}
/*eslint no-constant-condition: ["error", { "checkLoops": true }]*/
for (let x = 0; x <= 10; x++) {
  doSomething()
}
Example of correct code for when checkLoops is "allExceptWhileTrue":
/*eslint no-constant-condition: "error"*/
while (true) {
  doSomething()
}
Examples of correct code for when checkLoops is "none" or false:
/*eslint no-constant-condition: ["error", { "checkLoops": "none" }]*/
while (true) {
  doSomething()
  if (condition()) {
    break
  }
}
do {
  doSomething()
  if (condition()) {
    break
  }
} while (true)
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
while (true) {
  doSomething()
  if (condition()) {
    break
  }
}
for (; true; ) {
  doSomething()
  if (condition()) {
    break
  }
}
Rule Details
This rule disallows duplicate parameter names in function declarations or expressions. It does not apply to arrow functions or class methods, because the parser reports the error.
If ESLint parses code in strict mode, the parser (instead of this rule) reports the error.
Examples of incorrect code for this rule:
::: incorrect { "sourceType": "script" }
/*eslint no-dupe-args: "error"*/
function foo(a, b, a) {
  console.log('value of the second a:', a)
}
const bar = function (a, b, a) {
  console.log('value of the second a:', a)
}
Examples of correct code for this rule:
::: correct { "sourceType": "script" }
/*eslint no-dupe-args: "error"*/
function foo(a, b, c) {
  console.log(a, b, c)
}
const bar = function (a, b, c) {
  console.log(a, b, c)
}
const foo = {
  bar: 'baz',
  bar: 'qux',
}
Rule Details
This rule disallows duplicate keys in object literals.
Examples of incorrect code for this rule:
/*eslint no-dupe-keys: "error"*/
const foo = {
  bar: 'baz',
  bar: 'qux',
}
const bar = {
  bar: 'baz',
  bar: 'qux',
}
const baz = {
  0x1: 'baz',
  1: 'qux',
}
Examples of correct code for this rule:
/*eslint no-dupe-keys: "error"*/
const foo = {
  bar: 'baz',
  quxx: 'qux',
}
const obj = {
  __proto__: baz, // defines object's prototype
  ['__proto__']: qux, // defines a property named "__proto__"
}
// doesn't create any variables
const {
  a: {},
} = foo
In this code, no new variables are created because a is just a location helper while the {} is expected to contain the variables to create, such as:
// creates variable b
const {
  a: { b },
} = foo
In many cases, the empty object pattern is a mistake where the author intended to use a default value instead, such as:
// creates variable a
const { a = {} } = foo
The difference between these two patterns is subtle, especially because the problematic empty pattern looks just like an object literal.
Rule Details
This rule aims to flag any empty patterns in destructured objects and arrays, and as such, will report a problem whenever one is encountered.
Examples of incorrect code for this rule:
/*eslint no-empty-pattern: "error"*/
const {} = foo
const [] = foo
const {
  a: {},
} = foo
const {
  a: [],
} = foo
function foo({}) {}
function bar([]) {}
function baz({ a: {} }) {}
function qux({ a: [] }) {}
Examples of correct code for this rule:
/*eslint no-empty-pattern: "error"*/
const { a = {} } = foo
const { b = [] } = foo
function foo({ a = {} }) {}
function bar({ a = [] }) {}
Options
This rule has an object option for exceptions:
allowObjectPatternsAsParameters
Set to false by default. Setting this option to true allows empty object patterns as function parameters.
Note: This rule doesn't allow empty array patterns as function parameters.
Examples of incorrect code for this rule with the {"allowObjectPatternsAsParameters": true} option:
/*eslint no-empty-pattern: ["error", { "allowObjectPatternsAsParameters": true }]*/
function foo({ a: {} }) {}
const bar = function ({ a: {} }) {}
const qux = ({ a: {} }) => {}
const quux = ({} = bar) => {}
const item = ({} = { bar: 1 }) => {}
function baz([]) {}
Examples of correct code for this rule with the {"allowObjectPatternsAsParameters": true} option:
/*eslint no-empty-pattern: ["error", { "allowObjectPatternsAsParameters": true }]*/
function foo({}) {}
const bar = function ({}) {}
const qux = ({}) => {}
function baz({} = {}) {}
if (!!foo) {
  // ...
}
if (Boolean(foo)) {
  // ...
}
if (foo) {
  // ...
}
Rule Details
This rule disallows unnecessary boolean casts.
Examples of incorrect code for this rule:
/*eslint no-extra-boolean-cast: "error"*/
const foo = !!!bar
const foo1 = !!bar ? baz : bat
const foo2 = Boolean(!!bar)
const foo3 = new Boolean(!!bar)
if (!!foo) {
  // ...
}
if (Boolean(foo)) {
  // ...
}
while (!!foo) {
  // ...
}
do {
  // ...
} while (Boolean(foo))
for (; !!foo; ) {
  // ...
}
Examples of correct code for this rule:
/*eslint no-extra-boolean-cast: "error"*/
const foo = !!bar
const foo1 = Boolean(bar)
function qux() {
  return !!bar
}
foo = bar ? !!baz : !!bat
Options
This rule has an object option:
- "enforceForInnerExpressions"when set to- true, in addition to checking default contexts, checks whether extra boolean casts are present in expressions whose result is used in a boolean context. See examples below. Default is- false, meaning that this rule by default does not warn about extra booleans cast inside inner expressions.
Deprecated: The object property enforceForLogicalOperands is deprecated (eslint#18222). Please use enforceForInnerExpressions instead.
enforceForInnerExpressions
Examples of incorrect code for this rule with "enforceForInnerExpressions" option set to true:
/*eslint no-extra-boolean-cast: ["error", {"enforceForInnerExpressions": true}]*/
if (!!foo || bar) {
  //...
}
while (!!foo && bar) {
  //...
}
if ((!!foo || bar) && !!baz) {
  //...
}
const foo = new Boolean(!!bar || baz)
foo && Boolean(bar) ? baz : bat
const ternaryBranches = Boolean(bar ? !!baz : bat)
const nullishCoalescingOperator = Boolean(bar ?? Boolean(baz))
const commaOperator = Boolean((bar, baz, !!bat))
// another comma operator example
for (let i = 0; console.log(i), Boolean(i < 10); i++) {
  // ...
}
Examples of correct code for this rule with "enforceForInnerExpressions" option set to true:
/*eslint no-extra-boolean-cast: ["error", {"enforceForInnerExpressions": true}]*/
// Note that `||` and `&&` alone aren't a boolean context for either operand
// since the resultant value need not be a boolean without casting.
const foo = !!bar || baz
if (foo || bar) {
  //...
}
while (foo && bar) {
  //...
}
if ((foo || bar) && baz) {
  //...
}
const foo1 = new Boolean(bar || baz)
foo && bar ? baz : bat
const ternaryBranches = Boolean(bar ? baz : bat)
const nullishCoalescingOperator = Boolean(bar ?? baz)
const commaOperator = Boolean((bar, baz, bat))
// another comma operator example
for (let i = 0; console.log(i), i < 10; i++) {
  // ...
}
// comma operator in non-final position
Boolean((Boolean(bar), baz, bat))
Rule Details
This rule is aimed at eliminating variables that have multiple declarations in the same scope.
Examples of incorrect code for this rule:
/*eslint no-redeclare: "error"*/
var a = 3
var a = 10
class C {
  foo() {
    var b = 3
    var b = 10
  }
  static {
    var c = 3
    var c = 10
  }
}
Examples of correct code for this rule:
/*eslint no-redeclare: "error"*/
var a = 3
a = 10
class C {
  foo() {
    var b = 3
    b = 10
  }
  static {
    var c = 3
    c = 10
  }
}
Options
This rule takes one optional argument, an object with a boolean property "builtinGlobals". It defaults to true.
If set to true, this rule also checks redeclaration of built-in globals, such as Object, Array, Number...
builtinGlobals
The "builtinGlobals" option will check for redeclaration of built-in globals in global scope.
Examples of incorrect code for the { "builtinGlobals": true } option:
::: incorrect { "sourceType": "script" }
/*eslint no-redeclare: ["error", { "builtinGlobals": true }]*/
var Object = 0
Note that when using sourceType: "commonjs" (or ecmaFeatures.globalReturn, if using the default parser), the top scope of a program is not actually the global scope, but rather a "module" scope. When this is the case, declaring a variable named after a builtin global is not a redeclaration, but rather a shadowing of the global variable. In that case, the no-shadow rule with the "builtinGlobals" option should be used.
- return awaiton a promise will not result in an extra microtask.
- return awaityields a better stack trace for debugging.
Historical context: When promises were first introduced, calling return await introduced an additional microtask, one for the await and one for the return value of the async function. Each extra microtask delays the computation of a result and so this rule was added to help avoid this performance trap. Later, ECMA-262 changed the way return await worked so it would create a single microtask, which means this rule is no longer necessary.
Rule Details
This rule warns on any usage of return await except in try blocks.
Examples of incorrect code for this rule:
/*eslint no-return-await: "error"*/
async function foo() {
  return await bar()
}
Examples of correct code for this rule:
/*eslint no-return-await: "error"*/
async function foo1() {
  return bar()
}
async function foo2() {
  await bar()
  return
}
// This is essentially the same as `return await bar();`, but the rule checks only `await` in `return` statements
async function foo3() {
  const x = await bar()
  return x
}
// In this example the `await` is necessary to be able to catch errors thrown from `bar()`
async function foo4() {
  try {
    return await bar()
  } catch (error) {}
}
When Not To Use It
You should not use this rule. There is no reason to avoid return await.
Rule Details
Any reference to an undeclared variable causes a warning, unless the variable is explicitly mentioned in a /*global ...*/ comment, or specified in the globals key in the configuration file. A common use case for these is if you intentionally use globals that are defined elsewhere (e.g. in a script sourced from HTML).
Examples of incorrect code for this rule:
/*eslint no-undef: "error"*/
const foo = someFunction()
const bar = a + 1
Examples of correct code for this rule with global declaration:
/*global someFunction, a*/
/*eslint no-undef: "error"*/
const foo = someFunction()
const bar = a + 1
Note that this rule does not disallow assignments to read-only global variables. See no-global-assign if you also want to disallow those assignments.
This rule also does not disallow redeclarations of global variables. See no-redeclare if you also want to disallow those redeclarations.
Options
- typeofset to true will warn for variables used inside typeof check (Default false).
typeof
Examples of correct code for the default { "typeof": false } option:
/*eslint no-undef: "error"*/
if (typeof UndefinedIdentifier === 'undefined') {
  // do something ...
}
You can use this option if you want to prevent typeof check on a variable which has not been declared.
Examples of incorrect code for the { "typeof": true } option:
/*eslint no-undef: ["error", { "typeof": true }] */
if (typeof a === 'string') {
}
Examples of correct code for the { "typeof": true } option with global declaration:
/*global a*/
/*eslint no-undef: ["error", { "typeof": true }] */
if (typeof a === 'string') {
}
When Not To Use It
If explicit declaration of global variables is not to your taste.
Compatibility
This rule provides compatibility with treatment of global variables in JSHint and JSLint.
Here are some examples:
// Bad
const isYes = answer === 1 ? true : false
// Good
const isYes = answer === 1
// Bad
const isNo = answer === 1 ? false : true
// Good
const isNo = answer !== 1
Another common mistake is using a single variable as both the conditional test and the consequent. In such cases, the logical OR can be used to provide the same functionality.
Here is an example:
// Bad
foo(bar ? bar : 1)
// Good
foo(bar || 1)
Rule Details
This rule disallow ternary operators when simpler alternatives exist.
Examples of incorrect code for this rule:
/*eslint no-unneeded-ternary: "error"*/
const a = x === 2 ? true : false
const b = x ? true : false
Examples of correct code for this rule:
/*eslint no-unneeded-ternary: "error"*/
const a = x === 2 ? 'Yes' : 'No'
const b = x !== false
const c = x ? 'Yes' : 'No'
const d = x ? y : x
f(x ? x : 1) // default assignment - would be disallowed if defaultAssignment option set to false. See option details below.
Options
This rule has an object option:
- "defaultAssignment": true(default) allows the conditional expression as a default assignment pattern
- "defaultAssignment": falsedisallows the conditional expression as a default assignment pattern
defaultAssignment
When set to true, which it is by default, The defaultAssignment option allows expressions of the form x ? x : expr (where x is any identifier and expr is any expression).
Examples of additional incorrect code for this rule with the { "defaultAssignment": false } option:
/*eslint no-unneeded-ternary: ["error", { "defaultAssignment": false }]*/
const a = x ? x : 1
f(x ? x : 1)
Note that defaultAssignment: false still allows expressions of the form x ? expr : x (where the identifier is on the right hand side of the ternary).
When Not To Use It
You can turn this rule off if you are not concerned with unnecessary complexity in conditional expressions.
Rule Details
This rule disallows negating the left operand of the following relational operators:
Examples of incorrect code for this rule:
/*eslint no-unsafe-negation: "error"*/
if ((!key) in object) {
  // operator precedence makes it equivalent to (!key) in object
  // and type conversion makes it equivalent to (key ? "false" : "true") in object
}
if ((!obj) instanceof Ctor) {
  // operator precedence makes it equivalent to (!obj) instanceof Ctor
  // and it equivalent to always false since boolean values are not objects.
}
Examples of correct code for this rule:
/*eslint no-unsafe-negation: "error"*/
if (!(key in object)) {
  // key is not in object
}
if (!(obj instanceof Ctor)) {
  // obj is not an instance of Ctor
}
Exception
For rare situations when negating the left operand is intended, this rule allows an exception. If the whole negation is explicitly wrapped in parentheses, the rule will not report a problem.
Examples of correct code for this rule:
/*eslint no-unsafe-negation: "error"*/
if ((!foo) in object) {
  // allowed, because the negation is explicitly wrapped in parentheses
  // it is equivalent to (foo ? "false" : "true") in object
  // this is allowed as an exception for rare situations when that is the intended meaning
}
if ('' + !foo in object) {
  // you can also make the intention more explicit, with type conversion
}
Examples of incorrect code for this rule:
/*eslint no-unsafe-negation: "error"*/
if ((!foo) in object) {
  // this is not an allowed exception
}
Options
This rule has an object option:
- "enforceForOrderingRelations": false(default) allows negation of the left-hand side of ordering relational operators (- <,- >,- <=,- >=)
- "enforceForOrderingRelations": truedisallows negation of the left-hand side of ordering relational operators
enforceForOrderingRelations
With this option set to true the rule is additionally enforced for:
- <operator.
- >operator.
- <=operator.
- >=operator.
The purpose is to avoid expressions such as ! a < b (which is equivalent to (a ? 0 : 1) < b) when what is really intended is !(a < b).
Examples of additional incorrect code for this rule with the { "enforceForOrderingRelations": true } option:
/*eslint no-unsafe-negation: ["error", { "enforceForOrderingRelations": true }]*/
if (!a < b) {
}
while (!a > b) {}
foo = !a <= b
foo = !a >= b
When Not To Use It
If you don't want to notify unsafe logical negations, then it's safe to disable this rule.
For example, n + 1; is not a syntax error, but it might be a typing mistake where a programmer meant an assignment statement n += 1; instead. Sometimes, such unused expressions may be eliminated by some build tools in production environment, which possibly breaks application logic.
Rule Details
This rule aims to eliminate unused expressions which have no effect on the state of the program.
This rule does not apply to function calls or constructor calls with the new operator, because they could have side effects on the state of the program.
let i = 0
function increment() {
  i += 1
}
increment() // return value is unused, but i changed as a side effect
let nThings = 0
function Thing() {
  nThings += 1
}
new Thing() // constructed object is unused, but nThings changed as a side effect
This rule does not apply to directives (which are in the form of literal string expressions such as "use strict"; at the beginning of a script, module, or function) when using ES5+ environments. In ES3 environments, directives are treated as unused expressions by default, but this behavior can be changed using the ignoreDirectives option.
Sequence expressions (those using a comma, such as a = 1, b = 2) are always considered unused unless their return value is assigned or used in a condition evaluation, or a function call is made with the sequence expression value.
Options
This rule, in its default state, does not require any arguments. If you would like to enable one or more of the following you may pass an object with the options set as follows:
- allowShortCircuitset to- truewill allow you to use short circuit evaluations in your expressions (Default:- false).
- allowTernaryset to- truewill enable you to use ternary operators in your expressions similarly to short circuit evaluations (Default:- false).
- allowTaggedTemplatesset to- truewill enable you to use tagged template literals in your expressions (Default:- false).
- enforceForJSXset to- truewill flag unused JSX element expressions (Default:- false).
- ignoreDirectivesset to- truewill prevent directives from being reported as unused expressions when linting with- ecmaVersion: 3(Default:- false).
These options allow unused expressions only if all of the code paths either directly change the state (for example, assignment statement) or could have side effects (for example, function call).
Examples of incorrect code for the default { "allowShortCircuit": false, "allowTernary": false } options:
/*eslint no-unused-expressions: "error"*/
0
if (0) 0
{
  0
}
;(f(0), {})
a && b()
;(a, b())
;((c = a), b)
a() &&
  (function namedFunctionInExpressionContext() {
    f()
  })(function anIncompleteIIFE() {})
injectGlobal`body{ color: red; }`
Examples of correct code for the default { "allowShortCircuit": false, "allowTernary": false } options:
/*eslint no-unused-expressions: "error"*/
{
} // In this context, this is a block statement, not an object literal
{
  myLabel: foo()
} // In this context, this is a block statement with a label and expression, not an object literal
function namedFunctionDeclaration() {}
;(function aGenuineIIFE() {})()
f()
a = 0
new C()
delete a.b
void a
Note that one or more string expression statements (with or without semi-colons) will only be considered as unused if they are not in the beginning of a script, module, or function (alone and uninterrupted by other statements). Otherwise, they will be treated as part of a "directive prologue", a section potentially usable by JavaScript engines. This includes "strict mode" directives.
Examples of correct code for this rule in regard to directives:
/*eslint no-unused-expressions: "error"*/
'use strict'
'use asm'
'use stricter'
'use babel'
'any other strings like this in the directive prologue'
'this is still the directive prologue'
function foo() {
  'bar'
}
class Foo {
  someMethod() {
    'use strict'
  }
}
Examples of incorrect code for this rule in regard to directives:
/*eslint no-unused-expressions: "error"*/
doSomething()
;('use strict') // this isn't in a directive prologue, because there is a non-directive statement before it
function foo() {
  'bar' + 1
}
class Foo {
  static {
    'use strict' // class static blocks do not have directive prologues
  }
}
allowShortCircuit
Examples of incorrect code for the { "allowShortCircuit": true } option:
/*eslint no-unused-expressions: ["error", { "allowShortCircuit": true }]*/
a || b
Examples of correct code for the { "allowShortCircuit": true } option:
/*eslint no-unused-expressions: ["error", { "allowShortCircuit": true }]*/
a && b()
a() || (b = c)
allowTernary
Examples of incorrect code for the { "allowTernary": true } option:
/*eslint no-unused-expressions: ["error", { "allowTernary": true }]*/
a ? b : 0
a ? b : c()
Examples of correct code for the { "allowTernary": true } option:
/*eslint no-unused-expressions: ["error", { "allowTernary": true }]*/
a ? b() : c()
a ? (b = c) : d()
allowShortCircuit and allowTernary
Examples of correct code for the { "allowShortCircuit": true, "allowTernary": true } options:
/*eslint no-unused-expressions: ["error", { "allowShortCircuit": true, "allowTernary": true }]*/
a ? b() || (c = d) : e()
allowTaggedTemplates
Examples of incorrect code for the { "allowTaggedTemplates": true } option:
/*eslint no-unused-expressions: ["error", { "allowTaggedTemplates": true }]*/
;`some untagged template string`
Examples of correct code for the { "allowTaggedTemplates": true } option:
/*eslint no-unused-expressions: ["error", { "allowTaggedTemplates": true }]*/
tag`some tagged template string`
enforceForJSX
JSX is most-commonly used in the React ecosystem, where it is compiled to React.createElement expressions. Though free from side-effects, these calls are not automatically flagged by the no-unused-expression rule. If you're using React, or any other side-effect-free JSX pragma, this option can be enabled to flag these expressions.
Examples of incorrect code for the { "enforceForJSX": true } option:
::: incorrect { "parserOptions": { "ecmaFeatures": { "jsx": true } } }
/*eslint no-unused-expressions: ["error", { "enforceForJSX": true }]*/
;<MyComponent />
;<></>
Examples of correct code for the { "enforceForJSX": true } option:
::: correct { "parserOptions": { "ecmaFeatures": { "jsx": true } } }
/*eslint no-unused-expressions: ["error", { "enforceForJSX": true }]*/
const myComponentPartial = <MyComponent />
const myFragment = <></>
ignoreDirectives
When set to false (default), this rule reports directives (like "use strict") as unused expressions when linting with ecmaVersion: 3. This default behavior exists because ES3 environments do not formally support directives, meaning such strings are effectively unused expressions in that specific context.
Set this option to true to prevent directives from being reported as unused, even when ecmaVersion: 3 is specified. This option is primarily useful for projects that need to maintain a single codebase containing directives while supporting both older ES3 environments and modern (ES5+) environments.
Note: In ES5+ environments, directives are always ignored regardless of this setting.
Examples of incorrect code for the { "ignoreDirectives": false } option and ecmaVersion: 3:
::: incorrect { "ecmaVersion": 3, "sourceType": "script" }
/*eslint no-unused-expressions: ["error", { "ignoreDirectives": false }]*/
'use strict'
'use asm'
'use stricter'
'use babel'
'any other strings like this in the directive prologue'
'this is still the directive prologue'
function foo() {
  'bar'
}
Examples of correct code for the { "ignoreDirectives": true } option and ecmaVersion: 3:
::: correct { "ecmaVersion": 3, "sourceType": "script" }
/*eslint no-unused-expressions: ["error", { "ignoreDirectives": true }]*/
'use strict'
'use asm'
'use stricter'
'use babel'
'any other strings like this in the directive prologue'
'this is still the directive prologue'
function foo() {
  'bar'
}
TypeScript Support
This rule supports TypeScript-specific expressions and follows these guidelines:
- Directives (like 'use strict') are allowed in module and namespace declarations
- Type-related expressions are treated as unused if their wrapped value expressions are unused:
- Type assertions (x as number,<number>x)
- Non-null assertions (x!)
- Type instantiations (Set<number>)
 
- Type assertions (
Note: Although type expressions never have runtime side effects (e.g., x! is equivalent to x at runtime), they can be used to assert types for testing purposes.
Examples of correct code for this rule when using TypeScript:
/* eslint no-unused-expressions: "error" */
// Type expressions wrapping function calls are allowed
function getSet() {
  return Set
}
getSet()<number>
getSet() as Set<unknown>
getSet()!
// Directives in modules and namespaces
module Foo {
  'use strict'
  'hello world'
}
namespace Bar {
  'use strict'
  export class Baz {}
}
Examples of incorrect code for this rule when using TypeScript:
/* eslint no-unused-expressions: "error" */
// Standalone type expressions
Set<number>
1 as number
window!
// Expressions inside namespaces
namespace Bar {
  123
}
Rule Details
This rule reports unused private class members.
- A private field or method is considered to be unused if its value is never read.
- A private accessor is considered to be unused if it is never accessed (read or write).
Examples of incorrect code for this rule:
/*eslint no-unused-private-class-members: "error"*/
class A {
  #unusedMember = 5
}
class B {
  #usedOnlyInWrite = 5
  method() {
    this.#usedOnlyInWrite = 42
  }
}
class C {
  #usedOnlyToUpdateItself = 5
  method() {
    this.#usedOnlyToUpdateItself++
  }
}
class D {
  #unusedMethod() {}
}
class E {
  get #unusedAccessor() {}
  set #unusedAccessor(value) {}
}
Examples of correct code for this rule:
/*eslint no-unused-private-class-members: "error"*/
class A {
  #usedMember = 42
  method() {
    return this.#usedMember
  }
}
class B {
  #usedMethod() {
    return 42
  }
  anotherMethod() {
    return this.#usedMethod()
  }
}
class C {
  get #usedAccessor() {}
  set #usedAccessor(value) {}
  method() {
    this.#usedAccessor = 42
  }
}
When Not To Use It
If you don't want to be notified about unused private class members, you can safely turn this rule off.
Rule Details
This rule is aimed at eliminating unused variables, functions, and function parameters.
A variable foo is considered to be used if any of the following are true:
- It is called (foo()) or constructed (new foo())
- It is read (let bar = foo)
- It is passed into a function as an argument (doSomething(foo))
- It is read inside of a function that is passed to another function (doSomething(function() { foo(); }))
A variable is not considered to be used if it is only ever declared (let foo = 5) or assigned to (foo = 7).
Examples of incorrect code for this rule:
/*eslint no-unused-vars: "error"*/
/*global some_unused_var*/
// It checks variables you have defined as global
some_unused_var = 42
let x
// Write-only variables are not considered as used.
let y = 10
y = 5
// A read for a modification of itself is not considered as used.
let z = 0
z = z + 1
// By default, unused arguments cause warnings.
;(function (foo) {
  return 5
})()
// Unused recursive functions also cause warnings.
function fact(n) {
  if (n < 2) return 1
  return n * fact(n - 1)
}
// When a function definition destructures an array, unused entries from the array also cause warnings.
function getY([x, y]) {
  return y
}
getY(['a', 'b'])
Examples of correct code for this rule:
/*eslint no-unused-vars: "error"*/
const x = 10
alert(x)
// foo is considered used here
myFunc(
  function foo() {
    // ...
  }.bind(this),
)
;(function (foo) {
  return foo
})()
var myFunc
myFunc = setTimeout(function () {
  // myFunc is considered used
  myFunc()
}, 50)
// Only the second argument from the destructured array is used.
function getY([, y]) {
  return y
}
getY(['a', 'b'])
exported
In environments outside of CommonJS or ECMAScript modules, you may use var to create a global variable that may be used by other scripts. You can use the /* exported variableName */ comment block to indicate that this variable is being exported and therefore should not be considered unused.
Note that /* exported */ has no effect for any of the following:
- when languageOptions.sourceTypeismodule(default) orcommonjs
- when languageOptions.parserOptions.ecmaFeatures.globalReturnistrue
The line comment // exported variableName will not work as exported is not line-specific.
/* exported global_var */
var global_var = 42
Examples of correct code for /* exported variableName */ operation with no-unused-vars:
::: correct { "sourceType": "script" }
/*eslint no-unused-vars: "error"*/
/* exported global_var */
var global_var = 42
Options
This rule takes one argument which can be a string or an object. The string settings are the same as those of the vars property (explained below).
By default this rule is enabled with all option for caught errors and variables, and after-used for arguments.
{
  "rules": {
    "no-unused-vars": [
      "error",
      {
        "vars": "all",
        "args": "after-used",
        "caughtErrors": "all",
        "ignoreRestSiblings": false,
        "reportUsedIgnorePattern": false
      }
    ]
  }
}
vars
The vars option has two settings:
- allchecks all variables for usage, including those in the global scope. However, it excludes variables targeted by other options like- argsand- caughtErrors. This is the default setting.
- localchecks only that locally-declared variables are used but will allow global variables to be unused.
vars: local
Examples of correct code for the { "vars": "local" } option:
/*eslint no-unused-vars: ["error", { "vars": "local" }]*/
/*global some_unused_var */
some_unused_var = 42
varsIgnorePattern
The varsIgnorePattern option specifies exceptions not to check for usage: variables whose names match a regexp pattern. For example, variables whose names contain ignored or Ignored. However, it excludes variables targeted by other options like argsIgnorePattern and caughtErrorsIgnorePattern.
Examples of correct code for the { "varsIgnorePattern": "[iI]gnored" } option:
/*eslint no-unused-vars: ["error", { "varsIgnorePattern": "[iI]gnored" }]*/
const firstVarIgnored = 1
const secondVar = 2
console.log(secondVar)
args
The args option has three settings:
- after-used- unused positional arguments that occur before the last used argument will not be checked, but all named arguments and all positional arguments after the last used argument will be checked.
- all- all named arguments must be used.
- none- do not check arguments.
args: after-used
Examples of incorrect code for the default { "args": "after-used" } option:
/*eslint no-unused-vars: ["error", { "args": "after-used" }]*/
// 2 errors, for the parameters after the last used parameter (bar)
// "baz" is defined but never used
// "qux" is defined but never used
;(function (foo, bar, baz, qux) {
  return bar
})()
Examples of correct code for the default { "args": "after-used" } option:
/*eslint no-unused-vars: ["error", {"args": "after-used"}]*/
;(function (foo, bar, baz, qux) {
  return qux
})()
args: all
Examples of incorrect code for the { "args": "all" } option:
/*eslint no-unused-vars: ["error", { "args": "all" }]*/
// 2 errors
// "foo" is defined but never used
// "baz" is defined but never used
;(function (foo, bar, baz) {
  return bar
})()
args: none
Examples of correct code for the { "args": "none" } option:
/*eslint no-unused-vars: ["error", { "args": "none" }]*/
;(function (foo, bar, baz) {
  return bar
})()
argsIgnorePattern
The argsIgnorePattern option specifies exceptions not to check for usage: arguments whose names match a regexp pattern. For example, variables whose names begin with an underscore.
Examples of correct code for the { "argsIgnorePattern": "^_" } option:
/*eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }]*/
function foo(x, _y) {
  return x + 1
}
foo()
caughtErrors
The caughtErrors option is used for catch block arguments validation.
It has two settings:
- all- all named arguments must be used. This is the default setting.
- none- do not check error objects.
caughtErrors: all
Not specifying this option is equivalent of assigning it to all.
Examples of incorrect code for the { "caughtErrors": "all" } option:
/*eslint no-unused-vars: ["error", { "caughtErrors": "all" }]*/
// 1 error
// "err" is defined but never used
try {
  //...
} catch (err) {
  console.error('errors')
}
caughtErrors: none
Examples of correct code for the { "caughtErrors": "none" } option:
/*eslint no-unused-vars: ["error", { "caughtErrors": "none" }]*/
try {
  //...
} catch (err) {
  console.error('errors')
}
caughtErrorsIgnorePattern
The caughtErrorsIgnorePattern option specifies exceptions not to check for usage: catch arguments whose names match a regexp pattern. For example, variables whose names begin with a string 'ignore'.
Examples of correct code for the { "caughtErrorsIgnorePattern": "^ignore" } option:
/*eslint no-unused-vars: ["error", { "caughtErrors": "all", "caughtErrorsIgnorePattern": "^ignore" }]*/
try {
  //...
} catch (ignoreErr) {
  console.error('errors')
}
destructuredArrayIgnorePattern
The destructuredArrayIgnorePattern option specifies exceptions not to check for usage: elements of array destructuring patterns whose names match a regexp pattern. For example, variables whose names begin with an underscore.
Examples of correct code for the { "destructuredArrayIgnorePattern": "^_" } option:
/*eslint no-unused-vars: ["error", { "destructuredArrayIgnorePattern": "^_" }]*/
const [a, _b, c] = ['a', 'b', 'c']
console.log(a + c)
const {
  x: [_a, foo],
} = bar
console.log(foo)
function baz([_c, x]) {
  x
}
baz()
function test({ p: [_q, r] }) {
  r
}
test()
let _m, n
foo.forEach((item) => {
  ;[_m, n] = item
  console.log(n)
})
let _o, p
_o = 1
;[_o, p] = foo
p
ignoreRestSiblings
The ignoreRestSiblings option is a boolean (default: false). Using a Rest Property it is possible to "omit" properties from an object, but by default the sibling properties are marked as "unused". With this option enabled the rest property's siblings are ignored.
Examples of correct code for the { "ignoreRestSiblings": true } option:
/*eslint no-unused-vars: ["error", { "ignoreRestSiblings": true }]*/
// 'foo' and 'bar' were ignored because they have a rest property sibling.
const { foo, ...rest } = data
console.log(rest)
// OR
let bar
;({ bar, ...rest } = data)
ignoreClassWithStaticInitBlock
The ignoreClassWithStaticInitBlock option is a boolean (default: false). Static initialization blocks allow you to initialize static variables and execute code during the evaluation of a class definition, meaning the static block code is executed without creating a new instance of the class. When set to true, this option ignores classes containing static initialization blocks.
Examples of incorrect code for the { "ignoreClassWithStaticInitBlock": true } option
/*eslint no-unused-vars: ["error", { "ignoreClassWithStaticInitBlock": true }]*/
class Foo {
  static myProperty = 'some string'
  static mymethod() {
    return 'some string'
  }
}
class Bar {
  static {
    let baz // unused variable
  }
}
Examples of correct code for the { "ignoreClassWithStaticInitBlock": true } option
/*eslint no-unused-vars: ["error", { "ignoreClassWithStaticInitBlock": true }]*/
class Foo {
  static {
    let bar = 'some string'
    console.log(bar)
  }
}
reportUsedIgnorePattern
The reportUsedIgnorePattern option is a boolean (default: false).
Using this option will report variables that match any of the valid ignore
pattern options (varsIgnorePattern, argsIgnorePattern, caughtErrorsIgnorePattern, or
destructuredArrayIgnorePattern) if they have been used.
Examples of incorrect code for the { "reportUsedIgnorePattern": true } option:
/*eslint no-unused-vars: ["error", { "reportUsedIgnorePattern": true, "varsIgnorePattern": "[iI]gnored" }]*/
const firstVarIgnored = 1
const secondVar = 2
console.log(firstVarIgnored, secondVar)
Examples of correct code for the { "reportUsedIgnorePattern": true } option:
/*eslint no-unused-vars: ["error", { "reportUsedIgnorePattern": true, "varsIgnorePattern": "[iI]gnored" }]*/
const firstVar = 1
const secondVar = 2
console.log(firstVar, secondVar)
When Not To Use It
If you don't want to be notified about unused variables or function arguments, you can safely turn this rule off.
const declaration tells readers, "this variable is never reassigned," reducing cognitive load and improving maintainability.
Rule Details
This rule is aimed at flagging variables that are declared using let keyword, but never reassigned after the initial assignment.
Examples of incorrect code for this rule:
/*eslint prefer-const: "error"*/
// it's initialized and never reassigned.
let a = 3
console.log(a)
let b
b = 0
console.log(b)
class C {
  static {
    let a
    a = 0
    console.log(a)
  }
}
// `i` is redefined (not reassigned) on each loop step.
for (let i in [1, 2, 3]) {
  console.log(i)
}
// `a` is redefined (not reassigned) on each loop step.
for (let a of [1, 2, 3]) {
  console.log(a)
}
Examples of correct code for this rule:
/*eslint prefer-const: "error"*/
// using const.
const a = 0
// it's never initialized.
let b
console.log(b)
// it's reassigned after initialized.
let c
c = 0
c = 1
console.log(c)
// it's initialized in a different block from the declaration.
let d
if (true) {
  d = 0
}
console.log(d)
// it's initialized in a different scope.
let e
class C {
  #x
  static {
    e = (obj) => obj.#x
  }
}
// it's initialized at a place that we cannot write a variable declaration.
let f
if (true) f = 0
console.log(f)
// `i` gets a new binding each iteration
for (const i in [1, 2, 3]) {
  console.log(i)
}
// `a` gets a new binding each iteration
for (const a of [1, 2, 3]) {
  console.log(a)
}
// `end` is never reassigned, but we cannot separate the declarations without modifying the scope.
for (let i = 0, end = 10; i < end; ++i) {
  console.log(i)
}
// `predicate` is only assigned once but cannot be separately declared as `const`
let predicate
;[object.type, predicate] = foo()
// `g` is only assigned once but cannot be separately declared as `const`
let g
const h = {}
;({ g, c: h.c } = func())
// suggest to use `no-var` rule.
var i = 3
console.log(i)
Options
{
  "prefer-const": [
    "error",
    {
      "destructuring": "any",
      "ignoreReadBeforeAssign": false
    }
  ]
}
destructuring
The kind of the way to address variables in destructuring. There are 2 values:
- "any"(default) - If any variables in destructuring should be- const, this rule warns for those variables.
- "all"- If all variables in destructuring should be- const, this rule warns the variables. Otherwise, ignores them.
Examples of incorrect code for the default {"destructuring": "any"} option:
/*eslint prefer-const: "error"*/
let { a, b } = obj /*error 'b' is never reassigned, use 'const' instead.*/
a = a + 1
Examples of correct code for the default {"destructuring": "any"} option:
/*eslint prefer-const: "error"*/
// using const.
const { a: a0, b } = obj
const a = a0 + 1
// all variables are reassigned.
let { c, d } = obj
c = c + 1
d = d + 1
Examples of incorrect code for the {"destructuring": "all"} option:
/*eslint prefer-const: ["error", {"destructuring": "all"}]*/
// all of `a` and `b` should be const, so those are warned.
let { a, b } = obj /*error 'a' is never reassigned, use 'const' instead.
                             'b' is never reassigned, use 'const' instead.*/
Examples of correct code for the {"destructuring": "all"} option:
/*eslint prefer-const: ["error", {"destructuring": "all"}]*/
// 'b' is never reassigned, but all of `a` and `b` should not be const, so those are ignored.
let { a, b } = obj
a = a + 1
ignoreReadBeforeAssign
This is an option to avoid conflicting with no-use-before-define rule (without "nofunc" option).
If true is specified, this rule will ignore variables that are read between the declaration and the first assignment.
Default is false.
Examples of correct code for the {"ignoreReadBeforeAssign": true} option:
/*eslint prefer-const: ["error", {"ignoreReadBeforeAssign": true}]*/
let timer
function initialize() {
  if (foo()) {
    clearInterval(timer)
  }
}
timer = setInterval(initialize, 100)
Examples of correct code for the default {"ignoreReadBeforeAssign": false} option:
/*eslint prefer-const: ["error", {"ignoreReadBeforeAssign": false}]*/
const timer = setInterval(initialize, 100)
function initialize() {
  if (foo()) {
    clearInterval(timer)
  }
}
When Not To Use It
If you don't want to be notified about variables that are never reassigned after initial assignment, you can safely disable this rule.
Rule Details
This rule aims to ensure that Promises are only rejected with Error objects.
Options
This rule takes one optional object argument:
- allowEmptyReject: true(- falseby default) allows calls to- Promise.reject()with no arguments.
Examples of incorrect code for this rule:
/*eslint prefer-promise-reject-errors: "error"*/
Promise.reject('something bad happened')
Promise.reject(5)
Promise.reject()
new Promise(function (resolve, reject) {
  reject('something bad happened')
})
new Promise(function (resolve, reject) {
  reject()
})
Examples of correct code for this rule:
/*eslint prefer-promise-reject-errors: "error"*/
Promise.reject(new Error('something bad happened'))
Promise.reject(new TypeError('something bad happened'))
new Promise(function (resolve, reject) {
  reject(new Error('something bad happened'))
})
const foo = getUnknownValue()
Promise.reject(foo)
Examples of correct code for this rule with the allowEmptyReject: true option:
/*eslint prefer-promise-reject-errors: ["error", {"allowEmptyReject": true}]*/
Promise.reject()
new Promise(function (resolve, reject) {
  reject()
})
Known Limitations
Due to the limits of static analysis, this rule cannot guarantee that you will only reject Promises with Error objects. While the rule will report cases where it can guarantee that the rejection reason is clearly not an Error, it will not report cases where there is uncertainty about whether a given reason is an Error. For more information on this caveat, see the similar limitations in the no-throw-literal rule.
To avoid conflicts between rules, this rule does not report non-error values used in throw statements in async functions, even though these lead to Promise rejections. To lint for these cases, use the no-throw-literal rule.
When Not To Use It
If you're using custom non-error values as Promise rejection reasons, you can turn off this rule.
This confusion led to the suggestion that you always use the radix parameter to parseInt() to eliminate unintended consequences. So instead of doing this:
const num = parseInt('071') // 57
Do this:
const num = parseInt('071', 10) // 71
ECMAScript 5 changed the behavior of parseInt() so that it no longer autodetects octal literals and instead treats them as decimal literals. However, the differences between hexadecimal and decimal interpretation of the first parameter causes many developers to continue using the radix parameter to ensure the string is interpreted in the intended way.
On the other hand, if the code is targeting only ES5-compliant environments passing the radix 10 may be redundant. In such a case you might want to disallow using such a radix.
Rule Details
This rule is aimed at preventing the unintended conversion of a string to a number of a different base than intended or at preventing the redundant 10 radix if targeting modern environments only.
Options
There are two options for this rule:
- "always"enforces providing a radix (default)
- "as-needed"disallows providing the- 10radix
always
Examples of incorrect code for the default "always" option:
/*eslint radix: "error"*/
const num = parseInt('071')
const num1 = parseInt(someValue)
const num2 = parseInt('071', 'abc')
const num3 = parseInt('071', 37)
const num4 = parseInt()
Examples of correct code for the default "always" option:
/*eslint radix: "error"*/
const num = parseInt('071', 10)
const num1 = parseInt('071', 8)
const num2 = parseFloat(someValue)
as-needed
Examples of incorrect code for the "as-needed" option:
/*eslint radix: ["error", "as-needed"]*/
const num = parseInt('071', 10)
const num1 = parseInt('071', 'abc')
const num2 = parseInt()
Examples of correct code for the "as-needed" option:
/*eslint radix: ["error", "as-needed"]*/
const num = parseInt('071')
const num1 = parseInt('071', 8)
const num2 = parseFloat(someValue)
When Not To Use It
If you don't want to enforce either presence or omission of the 10 radix value you can turn this rule off.
let totalLength = 0
async function addLengthOfSinglePage(pageNum) {
  totalLength += await getPageLength(pageNum)
}
Promise.all([addLengthOfSinglePage(1), addLengthOfSinglePage(2)]).then(() => {
  console.log('The combined length of both pages is', totalLength)
})
This code looks like it will sum the results of calling getPageLength(1) and getPageLength(2), but in reality the final value of totalLength will only be the length of one of the two pages. The bug is in the statement totalLength += await getPageLength(pageNum);. This statement first reads an initial value of totalLength, then calls getPageLength(pageNum) and waits for that Promise to fulfill. Finally, it sets the value of totalLength to the sum of await getPageLength(pageNum) and the initial value of totalLength. If the totalLength variable is updated in a separate function call during the time that the getPageLength(pageNum) Promise is pending, that update will be lost because the new value is overwritten without being read.
One way to fix this issue would be to ensure that totalLength is read at the same time as it's updated, like this:
async function addLengthOfSinglePage(pageNum) {
  const lengthOfThisPage = await getPageLength(pageNum)
  totalLength += lengthOfThisPage
}
Another solution would be to avoid using a mutable variable reference at all:
Promise.all([getPageLength(1), getPageLength(2)]).then((pageLengths) => {
  const totalLength = pageLengths.reduce(
    (accumulator, length) => accumulator + length,
    0,
  )
  console.log('The combined length of both pages is', totalLength)
})
Rule Details
This rule aims to report assignments to variables or properties in cases where the assignments may be based on outdated values.
Variables
This rule reports an assignment to a variable when it detects the following execution flow in a generator or async function:
- The variable is read.
- A yieldorawaitpauses the function.
- After the function is resumed, a value is assigned to the variable from step 1.
The assignment in step 3 is reported because it may be incorrectly resolved because the value of the variable from step 1 may have changed between steps 2 and 3. In particular, if the variable can be accessed from other execution contexts (for example, if it is not a local variable and therefore other functions can change it), the value of the variable may have changed elsewhere while the function was paused in step 2.
Note that the rule does not report the assignment in step 3 in any of the following cases:
- If the variable is read again between steps 2 and 3.
- If the variable cannot be accessed while the function is paused (for example, if it's a local variable).
Examples of incorrect code for this rule:
/* eslint require-atomic-updates: error */
let result
async function foo() {
  result += await something
}
async function bar() {
  result = result + (await something)
}
async function baz() {
  result = result + doSomething(await somethingElse)
}
async function qux() {
  if (!result) {
    result = await initialize()
  }
}
function* generator() {
  result += yield
}
Examples of correct code for this rule:
/* eslint require-atomic-updates: error */
let result
async function foobar() {
  result = (await something) + result
}
async function baz() {
  const tmp = doSomething(await somethingElse)
  result += tmp
}
async function qux() {
  if (!result) {
    const tmp = await initialize()
    if (!result) {
      result = tmp
    }
  }
}
async function quux() {
  let localVariable = 0
  localVariable += await something
}
function* generator() {
  result = (yield) + result
}
Properties
This rule reports an assignment to a property through a variable when it detects the following execution flow in a generator or async function:
- The variable or object property is read.
- A yieldorawaitpauses the function.
- After the function is resumed, a value is assigned to a property.
This logic is similar to the logic for variables, but stricter because the property in step 3 doesn't have to be the same as the property in step 1. It is assumed that the flow depends on the state of the object as a whole.
Example of incorrect code for this rule:
/* eslint require-atomic-updates: error */
async function foo(obj) {
  if (!obj.done) {
    obj.something = await getSomething()
  }
}
Example of correct code for this rule:
/* eslint require-atomic-updates: error */
async function foo(obj) {
  if (!obj.done) {
    const tmp = await getSomething()
    if (!obj.done) {
      obj.something = tmp
    }
  }
}
Options
This rule has an object option:
- "allowProperties": When set to- true, the rule does not report assignments to properties. Default is- false.
allowProperties
Example of correct code for this rule with the { "allowProperties": true } option:
/* eslint require-atomic-updates: ["error", { "allowProperties": true }] */
async function foo(obj) {
  if (!obj.done) {
    obj.something = await getSomething()
  }
}
When Not To Use It
If you don't use async or generator functions, you don't need to enable this rule.
Rule Details
This rule enforces comparing typeof expressions to valid string literals.
Examples of incorrect code for this rule:
/*eslint valid-typeof: "error"*/
typeof foo === 'strnig'
typeof foo == 'undefimed'
typeof bar != 'nunber'
typeof bar !== 'fucntion'
Examples of correct code for this rule:
/*eslint valid-typeof: "error"*/
typeof foo === 'string'
typeof bar == 'undefined'
typeof foo === baz
typeof bar === typeof qux
Options
This rule has an object option:
- "requireStringLiterals": trueallows the comparison of- typeofexpressions with only string literals or other- typeofexpressions, and disallows comparisons to any other value. Default is- false.
requireStringLiterals
Examples of incorrect code with the { "requireStringLiterals": true } option:
/*eslint valid-typeof: ["error", { "requireStringLiterals": true }]*/
typeof foo === undefined
typeof bar == Object
typeof baz === 'strnig'
typeof qux === 'some invalid type'
typeof baz === anotherVariable
typeof foo == 5
Examples of correct code with the { "requireStringLiterals": true } option:
/*eslint valid-typeof: ["error", { "requireStringLiterals": true }]*/
typeof foo === 'undefined'
typeof bar == 'object'
typeof baz === 'string'
typeof bar === typeof qux
When Not To Use It
You may want to turn this rule off if you will be using the typeof operator on host objects.
If a default import is requested, this rule will report if there is no default export in the imported module.
For ES7, reports if a default is named and exported but is not found in the referenced module.
Note: for packages, the plugin will find exported names
from jsnext:main, if present in package.json.
Redux's npm module includes this key, and thereby is lintable, for example.
A module path that is ignored or not unambiguously an ES module will not be reported when imported.
Rule Details
Given:
// ./foo.js
export default function () {
  return 42
}
// ./bar.js
export function bar() {
  return null
}
// ./baz.js
module.exports = function () {
  /* ... */
}
// node_modules/some-module/index.js
exports.sharedFunction = function shared() {
  /* ... */
}
The following is considered valid:
import foo from './foo'
// assuming 'node_modules' are ignored (true by default)
import someModule from 'some-module'
...and the following cases are reported:
import bar from './bar' // no default export found in ./bar
import baz from './baz' // no default export found in ./baz
When Not To Use It
If you are using CommonJS and/or modifying the exported namespace of any module at runtime, you will likely see false positives with this rule.
This rule currently does not interpret module.exports = ... as a default export,
either, so such a situation will be reported in the importing module.
Further Reading
- Lee Byron's ES7 export proposal
- import/ignoresetting
- jsnext:main(Rollup)
Reports funny business with exports, like repeated exports of names or defaults.
Rule Details
export default class MyClass { /*...*/ } // Multiple default exports.
function makeClass() { return new MyClass(...arguments) }
export default makeClass // Multiple default exports.
or
export const foo = function () {
  /*...*/
} // Multiple exports of name 'foo'.
function bar() {
  /*...*/
}
export { bar as foo } // Multiple exports of name 'foo'.
In the case of named/default re-export, all n re-exports will be reported,
as at least n-1 of them are clearly mistakes, but it is not clear which one
(if any) is intended. Could be the result of copy/paste, code duplication with
intent to rename, etc.
Further Reading
- Lee Byron's ES7 export proposal
Verifies that all named imports are part of the set of named exports in the referenced module.
For export, verifies that all named exports exist in the referenced module.
Note: for packages, the plugin will find exported names
from jsnext:main (deprecated) or module, if present in package.json.
Redux's npm module includes this key, and thereby is lintable, for example.
A module path that is ignored or not unambiguously an ES module will not be reported when imported. Note that type imports and exports, as used by Flow, are always ignored.
Rule Details
Given:
// ./foo.js
export const foo = "I'm so foo"
The following is considered valid:
// ./bar.js
import { foo } from './foo'
// ES7 proposal
export { foo as bar } from './foo'
// node_modules without jsnext:main are not analyzed by default
// (import/ignore setting)
import { SomeNonsenseThatDoesntExist } from 'react'
...and the following are reported:
// ./baz.js
import { notFoo } from './foo'
// ES7 proposal
export { notFoo as defNotBar } from './foo'
// will follow 'jsnext:main', if available
import { dontCreateStore } from 'redux'
Settings
import/ignore can be provided as a setting to ignore certain modules (node_modules,
CoffeeScript, CSS if using Webpack, etc.).
Given:
# .eslintrc (YAML)
---
settings:
  import/ignore:
    - node_modules  # included by default, but replaced if explicitly configured
    - *.coffee$     # can't parse CoffeeScript (unless a custom polyglot parser was configured)
and
# ./whatever.coffee
exports.whatever = (foo) -> console.log foo
then the following is not reported:
// ./foo.js
// can't be analyzed, and ignored, so not reported
import { notWhatever } from './whatever'
When Not To Use It
If you are using CommonJS and/or modifying the exported namespace of any module at runtime, you will likely see false positives with this rule.
Further Reading
- import/ignoresetting
- jsnext:maindeprecation
- pkg.module(Rollup)
Ensures that there is no resolvable path back to this module via its dependencies.
This includes cycles of depth 1 (imported module imports me) to "∞" (or Infinity), if the
maxDepth option is not set.
// dep-b.js
import './dep-a.js'
export function b() {
  /* ... */
}
// dep-a.js
import { b } from './dep-b.js' // reported: Dependency cycle detected.
This rule does not detect imports that resolve directly to the linted module;
for that, see no-self-import.
This rule ignores type-only imports in Flow and TypeScript syntax (import type and import typeof), which have no runtime effect.
Rule Details
Options
By default, this rule only detects cycles for ES6 imports, but see the no-unresolved options as this rule also supports the same commonjs and amd flags. However, these flags only impact which import types are linted; the
import/export infrastructure only registers import statements in dependencies, so
cycles created by require within imported modules may not be detected.
maxDepth
There is a maxDepth option available to prevent full expansion of very deep dependency trees:
/*eslint import/no-cycle: [2, { maxDepth: 1 }]*/
// dep-c.js
import './dep-a.js'
// dep-b.js
import './dep-c.js'
export function b() {
  /* ... */
}
// dep-a.js
import { b } from './dep-b.js' // not reported as the cycle is at depth 2
This is not necessarily recommended, but available as a cost/benefit tradeoff mechanism for reducing total project lint time, if needed.
ignoreExternal
An ignoreExternal option is available to prevent the cycle detection to expand to external modules:
/*eslint import/no-cycle: [2, { ignoreExternal: true }]*/
// dep-a.js
import 'module-b/dep-b.js'
export function a() {
  /* ... */
}
// node_modules/module-b/dep-b.js
import { a } from './dep-a.js' // not reported as this module is external
Its value is false by default, but can be set to true for reducing total project lint time, if needed.
allowUnsafeDynamicCyclicDependency
This option disable reporting of errors if a cycle is detected with at least one dynamic import.
// bar.js
import { foo } from './foo'
export const bar = foo
// foo.js
export const foo = 'Foo'
export function getBar() {
  return import('./bar')
}
Cyclic dependency are always a dangerous anti-pattern as discussed extensively in #2265. Please be extra careful about using this option.
disableScc
This option disables a pre-processing step that calculates Strongly Connected Components, which are used for avoiding unnecessary work checking files in different SCCs for cycles.
However, under some configurations, this pre-processing may be more expensive than the time it saves.
When this option is true, we don't calculate any SCC graph, and check all files for cycles (leading to higher time-complexity). Default is false.
When Not To Use It
This rule is comparatively computationally expensive. If you are pressed for lint time, or don't think you have an issue with dependency cycles, you may not want this rule enabled.
Further Reading
- Original inspiring issue
- Rule to detect that module imports itself: no-self-import
- import/external-module-folderssetting
🔧 This rule is automatically fixable by the --fix CLI option.
Reports if a resolved path is imported more than once.
ESLint core has a similar rule (no-duplicate-imports), but this version
is different in two key ways:
- the paths in the source code don't have to exactly match, they just have to point to the same module on the filesystem. (i.e. ./fooand./foo.js)
- this version distinguishes Flow typeimports from standard imports. (#334)
Rule Details
Valid:
import SomeDefaultClass, * as names from './mod'
// Flow `type` import from same module is fine
import type SomeType from './mod'
...whereas here, both ./mod imports will be reported:
import SomeDefaultClass from './mod'
// oops, some other import separated these lines
import foo from './some-other-mod'
import * as names from './mod'
// will catch this too, assuming it is the same target module
import { something } from './mod.js'
The motivation is that this is likely a result of two developers importing different names from the same module at different times (and potentially largely different locations in the file.) This rule brings both (or n-many) to attention.
Query Strings
By default, this rule ignores query strings (i.e. paths followed by a question mark), and thus imports from ./mod?a and ./mod?b will be considered as duplicates. However you can use the option considerQueryString to handle them as different (primarily because browsers will resolve those imports differently).
Config:
"import/no-duplicates": ["error", {"considerQueryString": true}]
And then the following code becomes valid:
import minifiedMod from './mod?minify'
import noCommentsMod from './mod?comments=0'
import originalMod from './mod'
It will still catch duplicates when using the same module and the exact same query string:
import SomeDefaultClass from './mod?minify'
// This is invalid, assuming `./mod` and `./mod.js` are the same target:
import * from './mod.js?minify'
Inline Type imports
TypeScript 4.5 introduced a new feature that allows mixing of named value and type imports. In order to support fixing to an inline type import when duplicate imports are detected, prefer-inline can be set to true.
Config:
"import/no-duplicates": ["error", {"prefer-inline": true}]
❌ Invalid ["error", {"prefer-inline": true}]
import { AValue, type AType } from './mama-mia'
import type { BType } from './mama-mia'
import { CValue } from './papa-mia'
import type { CType } from './papa-mia'
✅ Valid with ["error", {"prefer-inline": true}]
import { AValue, type AType, type BType } from './mama-mia'
import { CValue, type CType } from './papa-mia'
When Not To Use It
If the core ESLint version is good enough (i.e. you're not using Flow and you are using import/extensions), keep it and don't use this.
If you like to split up imports across lines or may need to import a default and a namespace, you may not want to enable this rule.
Ensures an imported module can be resolved to a module on the local filesystem,
as defined by standard Node require.resolve behavior.
See settings for customization options for the resolution (i.e.
additional filetypes, NODE_PATH, etc.)
This rule can also optionally report on unresolved modules in CommonJS require('./foo') calls and AMD require(['./foo'], function (foo) {...}) and define(['./foo'], function (foo) {...}).
To enable this, send { commonjs: true/false, amd: true/false } as a rule option.
Both are disabled by default.
If you are using Webpack, see the section on resolvers.
Rule Details
Options
By default, only ES6 imports will be resolved:
/*eslint import/no-unresolved: 2*/
import x from './foo' // reports if './foo' cannot be resolved on the filesystem
If {commonjs: true} is provided, single-argument require calls will be resolved:
/*eslint import/no-unresolved: [2, { commonjs: true }]*/
const { default: x } = require('./foo') // reported if './foo' is not found
require(0) // ignored
require(['x', 'y'], function (x, y) {
  /*...*/
}) // ignored
Similarly, if { amd: true } is provided, dependency paths for define and require
calls will be resolved:
/*eslint import/no-unresolved: [2, { amd: true }]*/
define(['./foo'], function (foo) {
  /*...*/
}) // reported if './foo' is not found
require(['./foo'], function (foo) {
  /*...*/
}) // reported if './foo' is not found
const { default: x } = require('./foo') // ignored
Both may be provided, too:
/*eslint import/no-unresolved: [2, { commonjs: true, amd: true }]*/
const { default: x } = require('./foo') // reported if './foo' is not found
define(['./foo'], function (foo) {
  /*...*/
}) // reported if './foo' is not found
require(['./foo'], function (foo) {
  /*...*/
}) // reported if './foo' is not found
ignore
This rule has its own ignore list, separate from import/ignore. This is because you may want to know whether a module can be located, regardless of whether it can be parsed for exports: node_modules, CoffeeScript files, etc. are all good to resolve properly, but will not be parsed if configured as such via import/ignore.
To suppress errors from files that may not be properly resolved by your resolver settings, you may add an ignore key with an array of RegExp pattern strings:
/*eslint import/no-unresolved: [2, { ignore: ['\\.img$'] }]*/
import { x } from './mod' // may be reported, if not resolved to a module
import coolImg from '../../img/coolImg.img' // will not be reported, even if not found
caseSensitive
By default, this rule will report paths whose case do not match the underlying filesystem path, if the FS is not case-sensitive. To disable this behavior, set the caseSensitive option to false.
/*eslint import/no-unresolved: [2, { caseSensitive: true (default) | false }]*/
const { default: x } = require('./foo') // reported if './foo' is actually './Foo' and caseSensitive: true
caseSensitiveStrict
The caseSensitive option does not detect case for the current working directory. The caseSensitiveStrict option allows checking cwd in resolved path. By default, the option is disabled.
/*eslint import/no-unresolved: [2, { caseSensitiveStrict: true }]*/
// Absolute paths
import Foo from `/Users/fOo/bar/file.js` // reported, /Users/foo/bar/file.js
import Foo from `d:/fOo/bar/file.js` // reported, d:/foo/bar/file.js
// Relative paths, cwd is Users/foo/
import Foo from `./../fOo/bar/file.js` // reported
When Not To Use It
If you're using a module bundler other than Node or Webpack, you may end up with a lot of false positive reports of missing dependencies.
Further Reading
- Resolver plugins
- Node resolver (default)
- Webpack resolver
- import/ignoreglobal setting
Use this rule to prevent unnecessary path segments in import and require statements.
Rule Details
Given the following folder structure:
my-project
├── app.js
├── footer.js
├── header.js
└── helpers.js
└── helpers
    └── index.js
├── index.js
└── pages
    ├── about.js
    ├── contact.js
    └── index.js
The following patterns are considered problems:
/**
 *  in my-project/app.js
 */
import './../my-project/pages/about.js' // should be "./pages/about.js"
import './../my-project/pages/about' // should be "./pages/about"
import '../my-project/pages/about.js' // should be "./pages/about.js"
import '../my-project/pages/about' // should be "./pages/about"
import './pages//about' // should be "./pages/about"
import './pages/' // should be "./pages"
import './pages/index' // should be "./pages" (except if there is a ./pages.js file)
import './pages/index.js' // should be "./pages" (except if there is a ./pages.js file)
The following patterns are NOT considered problems:
/**
 *  in my-project/app.js
 */
import './header.js'
import './pages'
import './pages/about'
import '.'
import '..'
import fs from 'fs'
Options
noUselessIndex
If you want to detect unnecessary /index or /index.js (depending on the specified file extensions, see below) imports in your paths, you can enable the option noUselessIndex. By default it is set to false:
"import/no-useless-path-segments": ["error", {
  noUselessIndex: true,
}]
Additionally to the patterns described above, the following imports are considered problems if noUselessIndex is enabled:
// in my-project/app.js
import './helpers/index' // should be "./helpers/" (not auto-fixable to `./helpers` because this would lead to an ambiguous import of `./helpers.js` and `./helpers/index.js`)
import './pages/index' // should be "./pages" (auto-fixable)
import './pages/index.js' // should be "./pages" (auto-fixable)
Note: noUselessIndex only avoids ambiguous imports for .js files if you haven't specified other resolved file extensions. See Settings: import/extensions for details.
commonjs
When set to true, this rule checks CommonJS imports. Default to false.
Enforce a convention in the order of require() / import statements.
With the groups option set to ["builtin", "external", "internal", "parent", "sibling", "index", "object", "type"] the order is as shown in the following example:
// 1. node "builtin" modules
import fs from 'fs'
import path from 'path'
// 2. "external" modules
import _ from 'lodash'
import chalk from 'chalk'
// 3. "internal" modules
// (if you have configured your path or webpack to handle your internal paths differently)
import foo from 'src/foo'
// 4. modules from a "parent" directory
import foo from '../foo'
import qux from '../../foo/qux'
// 5. "sibling" modules from the same or a sibling's directory
import bar from './bar'
import baz from './bar/baz'
// 6. "index" of the current directory
import main from './'
// 7. "object"-imports (only available in TypeScript)
import log = console.log
// 8. "type" imports (only available in Flow and TypeScript)
import type { Foo } from 'foo'
See here for further details on how imports are grouped.
Fail
import _ from 'lodash'
import path from 'path' // `path` import should occur before import of `lodash`
// -----
var _ = require('lodash')
var path = require('path') // `path` import should occur before import of `lodash`
// -----
var path = require('path')
import foo from './foo' // `import` statements must be before `require` statement
Pass
import path from 'path'
import _ from 'lodash'
// -----
var path = require('path')
var _ = require('lodash')
// -----
// Allowed as ̀`babel-register` is not assigned.
require('babel-register')
var path = require('path')
// -----
// Allowed as `import` must be before `require`
import foo from './foo'
var path = require('path')
Limitations of --fix
Unbound imports are assumed to have side effects, and will never be moved/reordered. This can cause other imports to get "stuck" around them, and the fix to fail.
import b from 'b'
import 'format.css' // This will prevent --fix from working.
import a from 'a'
As a workaround, move unbound imports to be entirely above or below bound ones.
import 'format1.css' // OK
import b from 'b'
import a from 'a'
import 'format2.css' // OK
Options
This rule supports the following options (none of which are required):
- groups
- pathGroups
- pathGroupsExcludedImportTypes
- distinctGroup
- newlines-between
- alphabetize
- named
- warnOnUnassignedImports
- sortTypesGroup
- newlines-between-types
- consolidateIslands
groups
Valid values: ("builtin" | "external" | "internal" | "unknown" | "parent" | "sibling" | "index" | "object" | "type")[] 
Default: ["builtin", "external", "parent", "sibling", "index"]
Determines which imports are subject to ordering, and how to order
them. The predefined groups are: "builtin", "external", "internal",
"unknown", "parent", "sibling", "index", "object", and "type".
The import order enforced by this rule is the same as the order of each group
in groups. Imports belonging to groups omitted from groups are lumped
together at the end.
Example
{
  "import/order": [
    "error",
    {
      "groups": [
        // Imports of builtins are first
        "builtin",
        // Then sibling and parent imports. They can be mingled together
        ["sibling", "parent"],
        // Then index file imports
        "index",
        // Then any arcane TypeScript imports
        "object",
        // Then the omitted imports: internal, external, type, unknown
      ],
    },
  ],
}
How Imports Are Grouped
An import (a ImportDeclaration, TSImportEqualsDeclaration, or require() CallExpression) is grouped by its type ("require" vs "import"), its specifier, and any corresponding identifiers.
import { identifier1, identifier2 } from 'specifier1'
import type { MyType } from 'specifier2'
const identifier3 = require('specifier3')
Roughly speaking, the grouping algorithm is as follows:
- If the import has no corresponding identifiers (e.g. import './my/thing.js'), is otherwise "unassigned," or is an unsupported use ofrequire(), andwarnOnUnassignedImportsis disabled, it will be ignored entirely since the order of these imports may be important for their side-effects
- If the import is part of an arcane TypeScript declaration (e.g. import log = console.log), it will be considered object. However, note that external module references (e.g.import x = require('z')) are treated as normalrequire()s and import-exports (e.g.export import w = y;) are ignored entirely
- If the import is type-only, "type"is ingroups, andsortTypesGroupis disabled, it will be considered type (with additional implications if usingpathGroupsand"type"is inpathGroupsExcludedImportTypes)
- If the import's specifier matches import/internal-regex, it will be considered internal
- If the import's specifier is an absolute path, it will be considered unknown
- If the import's specifier has the name of a Node.js core module (using is-core-module), it will be considered builtin
- If the import's specifier matches import/core-modules, it will be considered builtin
- If the import's specifier is a path relative to the parent directory of its containing file (e.g. starts with ../), it will be considered parent
- If the import's specifier is one of ['.', './', './index', './index.js'], it will be considered index
- If the import's specifier is a path relative to its containing file (e.g. starts with ./), it will be considered sibling
- If the import's specifier is a path pointing to a file outside the current package's root directory (determined using package-up), it will be considered external
- If the import's specifier matches import/external-module-folders(defaults to matching anything pointing to files within the current package'snode_modulesdirectory), it will be considered external
- If the import's specifier is a path pointing to a file within the current package's root directory (determined using package-up), it will be considered internal
- If the import's specifier has a name that looks like a scoped package (e.g. @scoped/package-name), it will be considered external
- If the import's specifier has a name that starts with a word character, it will be considered external
- If this point is reached, the import will be ignored entirely
At the end of the process, if they co-exist in the same file, all top-level require() statements that haven't been ignored are shifted (with respect to their order) below any ES6 import or similar declarations. Finally, any type-only declarations are potentially reorganized according to sortTypesGroup.
pathGroups
Valid values: PathGroup[] 
Default: []
Sometimes the predefined groups are not fine-grained enough, especially when using import aliases.
pathGroups defines one or more PathGroups relative to a predefined group.
Imports are associated with a PathGroup based on path matching against the import specifier (using minimatch).
[!IMPORTANT]
Note that, by default, imports grouped as
"builtin","external", or"object"will not be considered for furtherpathGroupsmatching unless they are removed frompathGroupsExcludedImportTypes.
PathGroup
| property | required | type | description | 
|---|---|---|---|
| pattern | ☑️ | string | Minimatch pattern for specifier matching | 
| patternOptions | object | Minimatch options; default: {nocomment: true} | |
| group | ☑️ | predefined group | One of the predefined groups to which matching imports will be positioned relatively | 
| position | "after" | "before" | Where, in relation to group, matching imports will be positioned; default: same position asgroup(neither before or after) | 
Example
{
  "import/order": [
    "error",
    {
      "pathGroups": [
        {
          // Minimatch pattern used to match against specifiers
          "pattern": "~/**",
          // The predefined group this PathGroup is defined in relation to
          "group": "external",
          // How matching imports will be positioned relative to "group"
          "position": "after",
        },
      ],
    },
  ],
}
pathGroupsExcludedImportTypes
Valid values: ("builtin" | "external" | "internal" | "unknown" | "parent" | "sibling" | "index" | "object" | "type")[] 
Default: ["builtin", "external", "object"]
By default, imports in certain groups are excluded from being matched against pathGroups to prevent overeager sorting.
Use pathGroupsExcludedImportTypes to modify which groups are excluded.
[!TIP]
If using imports with custom specifier aliases (e.g. you're using
eslint-import-resolver-alias,pathsintsconfig.json, etc) that end up grouped as"builtin"or"external"imports, remove them frompathGroupsExcludedImportTypesto ensure they are ordered correctly.
Example
{
  "import/order": [
    "error",
    {
      "pathGroups": [
        {
          "pattern": "@app/**",
          "group": "external",
          "position": "after",
        },
      ],
      "pathGroupsExcludedImportTypes": ["builtin"],
    },
  ],
}
distinctGroup
Valid values: boolean 
Default: true
[!CAUTION]
Currently,
distinctGroupdefaults totrue. However, in a later update, the default will change tofalse.
This changes how PathGroup.position affects grouping, and is most useful when newlines-between is set to always and at least one PathGroup has a position property set.
When newlines-between is set to always and an import matching a specific PathGroup.pattern is encountered, that import is added to a sort of "sub-group" associated with that PathGroup. Thanks to newlines-between, imports in this "sub-group" will have a new line separating them from the rest of the imports in PathGroup.group.
This behavior can be undesirable when using PathGroup.position to order imports within PathGroup.group instead of creating a distinct "sub-group". Set distinctGroup to false to disable the creation of these "sub-groups".
Example
{
  "import/order": [
    "error",
    {
      "distinctGroup": false,
      "newlines-between": "always",
      "pathGroups": [
        {
          "pattern": "@app/**",
          "group": "external",
          "position": "after",
        },
      ],
    },
  ],
}
newlines-between
Valid values: "ignore" | "always" | "always-and-inside-groups" | "never" 
Default: "ignore"
Enforces or forbids new lines between import groups.
- 
If set to ignore, no errors related to new lines between import groups will be reported
- 
If set to always, at least one new line between each group will be enforced, and new lines inside a group will be forbidden
[!TIP]
To prevent multiple lines between imports, the
no-multiple-empty-linesrule, or a tool like Prettier, can be used.
- 
If set to always-and-inside-groups, it will act likealwaysexcept new lines are allowed inside import groups
- 
If set to never, no new lines are allowed in the entire import section
Example
With the default groups setting, the following will fail the rule check:
/* eslint import/order: ["error", {"newlines-between": "always"}] */
import fs from 'fs'
import path from 'path'
import sibling from './foo'
import index from './'
/* eslint import/order: ["error", {"newlines-between": "always-and-inside-groups"}] */
import fs from 'fs'
import path from 'path'
import sibling from './foo'
import index from './'
/* eslint import/order: ["error", {"newlines-between": "never"}] */
import fs from 'fs'
import path from 'path'
import sibling from './foo'
import index from './'
While this will pass:
/* eslint import/order: ["error", {"newlines-between": "always"}] */
import fs from 'fs'
import path from 'path'
import sibling from './foo'
import index from './'
/* eslint import/order: ["error", {"newlines-between": "always-and-inside-groups"}] */
import fs from 'fs'
import path from 'path'
import sibling from './foo'
import index from './'
/* eslint import/order: ["error", {"newlines-between": "never"}] */
import fs from 'fs'
import path from 'path'
import sibling from './foo'
import index from './'
alphabetize
Valid values: { order?: "asc" | "desc" | "ignore", orderImportKind?: "asc" | "desc" | "ignore", caseInsensitive?: boolean } 
Default: { order: "ignore", orderImportKind: "ignore", caseInsensitive: false }
Determine the sort order of imports within each predefined group or PathGroup alphabetically based on specifier.
[!NOTE]
Imports will be alphabetized based on their specifiers, not by their identifiers. For example,
const a = require('z');will come afterconst z = require('a');whenalphabetizeis set to{ order: "asc" }.
Valid properties and their values include:
- 
order: use"asc"to sort in ascending order,"desc"to sort in descending order, or "ignore" to prevent sorting
- 
orderImportKind: use"asc"to sort various import kinds, e.g. type-only and typeof imports, in ascending order,"desc"to sort them in descending order, or "ignore" to prevent sorting
- 
caseInsensitive: usetrueto ignore case andfalseto consider case when sorting
Example
Given the following settings:
{
  "import/order": [
    "error",
    {
      "alphabetize": {
        "order": "asc",
        "caseInsensitive": true,
      },
    },
  ],
}
This will fail the rule check:
import React, { PureComponent } from 'react'
import aTypes from 'prop-types'
import { compose, apply } from 'xcompose'
import * as classnames from 'classnames'
import blist from 'BList'
While this will pass:
import blist from 'BList'
import * as classnames from 'classnames'
import aTypes from 'prop-types'
import React, { PureComponent } from 'react'
import { compose, apply } from 'xcompose'
named
Valid values: boolean | { enabled: boolean, import?: boolean, export?: boolean, require?: boolean, cjsExports?: boolean, types?: "mixed" | "types-first" | "types-last" } 
Default: false
Enforce ordering of names within imports and exports.
If set to true or { enabled: true }, all named imports must be ordered according to alphabetize.
If set to false or { enabled: false }, named imports can occur in any order.
If set to { enabled: true, ... }, and any of the properties import, export, require, or cjsExports are set to false, named ordering is disabled with respect to the following kind of expressions:
- import:
import { Readline } from 'readline'
- export:
export { Readline }
// and
export { Readline } from 'readline'
- require:
const { Readline } = require('readline')
- cjsExports:
module.exports.Readline = Readline
// and
module.exports = { Readline }
Further, the named.types option allows you to specify the order of import identifiers with inline type qualifiers (or "type-only" identifiers/names), e.g. import { type TypeIdentifier1, normalIdentifier2 } from 'specifier';.
named.types accepts the following values:
- types-first: forces type-only identifiers to occur first
- types-last: forces type-only identifiers to occur last
- mixed: sorts all identifiers in alphabetical order
Example
Given the following settings:
{
  "import/order": [
    "error",
    {
      "named": true,
      "alphabetize": {
        "order": "asc",
      },
    },
  ],
}
This will fail the rule check:
import { compose, apply } from 'xcompose'
While this will pass:
import { apply, compose } from 'xcompose'
warnOnUnassignedImports
Valid values: boolean 
Default: false
Warn when "unassigned" imports are out of order.
Unassigned imports are imports with no corresponding identifiers (e.g. import './my/thing.js' or require('./side-effects.js')).
[!NOTE]
These warnings are not fixable with
--fixsince unassigned imports might be used for their side-effects, and changing the order of such imports cannot be done safely.
Example
Given the following settings:
{
  "import/order": [
    "error",
    {
      "warnOnUnassignedImports": true,
    },
  ],
}
This will fail the rule check:
import fs from 'fs'
import './styles.css'
import path from 'path'
While this will pass:
import fs from 'fs'
import path from 'path'
import './styles.css'
sortTypesGroup
Valid values: boolean 
Default: false
[!NOTE]
This setting is only meaningful when
"type"is included ingroups.
Sort type-only imports separately from normal non-type imports.
When enabled, the intragroup sort order of type-only imports will mirror the intergroup ordering of normal imports as defined by groups, pathGroups, etc.
Example
Given the following settings:
{
  "import/order": [
    "error",
    {
      "groups": ["type", "builtin", "parent", "sibling", "index"],
      "alphabetize": { "order": "asc" },
    },
  ],
}
This will fail the rule check even though it's logically ordered as we expect (builtins come before parents, parents come before siblings, siblings come before indices), the only difference is we separated type-only imports from normal imports:
import type A from 'fs'
import type B from 'path'
import type C from '../foo.js'
import type D from './bar.js'
import type E from './'
import a from 'fs'
import b from 'path'
import c from '../foo.js'
import d from './bar.js'
import e from './'
This happens because type-only imports are considered part of one global
"type" group by default. However, if we set
sortTypesGroup to true:
{
  "import/order": [
    "error",
    {
      "groups": ["type", "builtin", "parent", "sibling", "index"],
      "alphabetize": { "order": "asc" },
      "sortTypesGroup": true,
    },
  ],
}
The same example will pass.
newlines-between-types
Valid values: "ignore" | "always" | "always-and-inside-groups" | "never" 
Default: the value of newlines-between
[!NOTE]
This setting is only meaningful when
sortTypesGroupis enabled.
newlines-between-types is functionally identical to newlines-between except it only enforces or forbids new lines between type-only import groups, which exist only when sortTypesGroup is enabled.
In addition, when determining if a new line is enforceable or forbidden between the type-only imports and the normal imports, newlines-between-types takes precedence over newlines-between.
Example
Given the following settings:
{
  "import/order": [
    "error",
    {
      "groups": ["type", "builtin", "parent", "sibling", "index"],
      "sortTypesGroup": true,
      "newlines-between": "always",
    },
  ],
}
This will fail the rule check:
import type A from 'fs'
import type B from 'path'
import type C from '../foo.js'
import type D from './bar.js'
import type E from './'
import a from 'fs'
import b from 'path'
import c from '../foo.js'
import d from './bar.js'
import e from './'
However, if we set newlines-between-types to "ignore":
{
  "import/order": [
    "error",
    {
      "groups": ["type", "builtin", "parent", "sibling", "index"],
      "sortTypesGroup": true,
      "newlines-between": "always",
      "newlines-between-types": "ignore",
    },
  ],
}
The same example will pass.
Note the new line after import type E from './'; but before import a from "fs";. This new line separates the type-only imports from the normal imports. Its existence is governed by newlines-between-types and not newlines-between.
[!IMPORTANT]
In certain situations,
consolidateIslands: truewill take precedence overnewlines-between-types: "never", if used, when it comes to the new line separating type-only imports from normal imports.
The next example will pass even though there's a new line preceding the normal import and newlines-between is set to "never":
{
  "import/order": [
    "error",
    {
      "groups": ["type", "builtin", "parent", "sibling", "index"],
      "sortTypesGroup": true,
      "newlines-between": "never",
      "newlines-between-types": "always",
    },
  ],
}
import type A from 'fs'
import type B from 'path'
import type C from '../foo.js'
import type D from './bar.js'
import type E from './'
import a from 'fs'
import b from 'path'
import c from '../foo.js'
import d from './bar.js'
import e from './'
While the following fails due to the new line between the last type import and the first normal import:
{
  "import/order": [
    "error",
    {
      "groups": ["type", "builtin", "parent", "sibling", "index"],
      "sortTypesGroup": true,
      "newlines-between": "always",
      "newlines-between-types": "never",
    },
  ],
}
import type A from 'fs'
import type B from 'path'
import type C from '../foo.js'
import type D from './bar.js'
import type E from './'
import a from 'fs'
import b from 'path'
import c from '../foo.js'
import d from './bar.js'
import e from './'
consolidateIslands
Valid values: "inside-groups" | "never" 
Default: "never"
[!NOTE]
This setting is only meaningful when
newlines-betweenand/ornewlines-between-typesis set to"always-and-inside-groups".
When set to "inside-groups", this ensures imports spanning multiple lines are separated from other imports with a new line while single-line imports are grouped together (and the space between them consolidated) if they belong to the same group or pathGroups.
[!IMPORTANT]
When all of the following are true:
sortTypesGroupis set totrue
consolidateIslandsis set to"inside-groups"
newlines-betweenis set to"always-and-inside-groups"whennewlines-between-typesis set to"never"(or vice-versa)Then
newlines-between/newlines-between-typeswill yield toconsolidateIslandsand allow new lines to separate multi-line imports regardless of the"never"setting.This configuration is useful, for instance, to keep single-line type-only imports stacked tightly together at the bottom of your import block to preserve space while still logically organizing normal imports for quick and pleasant reference.
Example
Given the following settings:
{
  "import/order": [
    "error",
    {
      "newlines-between": "always-and-inside-groups",
      "consolidateIslands": "inside-groups",
    },
  ],
}
This will fail the rule check:
var fs = require('fs')
var path = require('path')
var { util1, util2, util3 } = require('util')
var async = require('async')
var relParent1 = require('../foo')
var { relParent21, relParent22, relParent23, relParent24 } = require('../')
var relParent3 = require('../bar')
var { sibling1, sibling2, sibling3 } = require('./foo')
var sibling2 = require('./bar')
var sibling3 = require('./foobar')
While this will succeed (and is what --fix would yield):
var fs = require('fs')
var path = require('path')
var { util1, util2, util3 } = require('util')
var async = require('async')
var relParent1 = require('../foo')
var { relParent21, relParent22, relParent23, relParent24 } = require('../')
var relParent3 = require('../bar')
var { sibling1, sibling2, sibling3 } = require('./foo')
var sibling2 = require('./bar')
var sibling3 = require('./foobar')
Note the intragroup "islands" of grouped single-line imports, as well as multi-line imports, are surrounded by new lines. At the same time, note the typical new lines separating different groups are still maintained thanks to newlines-between.
The same holds true for the next example; when given the following settings:
{
  "import/order": [
    "error",
    {
      "alphabetize": { "order": "asc" },
      "groups": ["external", "internal", "index", "type"],
      "pathGroups": [
        {
          "pattern": "dirA/**",
          "group": "internal",
          "position": "after",
        },
        {
          "pattern": "dirB/**",
          "group": "internal",
          "position": "before",
        },
        {
          "pattern": "dirC/**",
          "group": "internal",
        },
      ],
      "newlines-between": "always-and-inside-groups",
      "newlines-between-types": "never",
      "pathGroupsExcludedImportTypes": [],
      "sortTypesGroup": true,
      "consolidateIslands": "inside-groups",
    },
  ],
}
[!IMPORTANT]
Pay special attention to the value of
pathGroupsExcludedImportTypesin this example's settings. Without it, the successful example below would fail. This is because the imports with specifiers starting with "dirA/", "dirB/", and "dirC/" are all considered part of the"external"group, and imports in that group are excluded frompathGroupsmatching by default.The fix is to remove
"external"(and, in this example, the others) frompathGroupsExcludedImportTypes.
This will fail the rule check:
import c from 'Bar'
import d from 'bar'
import { aa, bb, cc, dd, ee, ff, gg } from 'baz'
import { hh, ii, jj, kk, ll, mm, nn } from 'fizz'
import a from 'foo'
import b from 'dirA/bar'
import index from './'
import type { AA, BB, CC } from 'abc'
import type { Z } from 'fizz'
import type { A, B } from 'foo'
import type { C2 } from 'dirB/Bar'
import type { D2, X2, Y2 } from 'dirB/bar'
import type { E2 } from 'dirB/baz'
import type { C3 } from 'dirC/Bar'
import type { D3, X3, Y3 } from 'dirC/bar'
import type { E3 } from 'dirC/baz'
import type { F3 } from 'dirC/caz'
import type { C1 } from 'dirA/Bar'
import type { D1, X1, Y1 } from 'dirA/bar'
import type { E1 } from 'dirA/baz'
import type { F } from './index.js'
import type { G } from './aaa.js'
import type { H } from './bbb'
While this will succeed (and is what --fix would yield):
import c from 'Bar'
import d from 'bar'
import { aa, bb, cc, dd, ee, ff, gg } from 'baz'
import { hh, ii, jj, kk, ll, mm, nn } from 'fizz'
import a from 'foo'
import b from 'dirA/bar'
import index from './'
import type { AA, BB, CC } from 'abc'
import type { Z } from 'fizz'
import type { A, B } from 'foo'
import type { C2 } from 'dirB/Bar'
import type { D2, X2, Y2 } from 'dirB/bar'
import type { E2 } from 'dirB/baz'
import type { C3 } from 'dirC/Bar'
import type { D3, X3, Y3 } from 'dirC/bar'
import type { E3 } from 'dirC/baz'
import type { F3 } from 'dirC/caz'
import type { C1 } from 'dirA/Bar'
import type { D1, X1, Y1 } from 'dirA/bar'
import type { E1 } from 'dirA/baz'
import type { F } from './index.js'
import type { G } from './aaa.js'
import type { H } from './bbb'
Related
A "Thenable" value is an object which has a then method, such as a Promise.
The await keyword is generally used to retrieve the result of calling a Thenable's then method.
If the await keyword is used on a value that is not a Thenable, the value is directly resolved, but will still pause execution until the next microtask.
While doing so is valid JavaScript, it is often a programmer error, such as forgetting to add parenthesis to call a function that returns a Promise.
Examples
await 'value'
const createValue = () => 'value'
await createValue()
await Promise.resolve('value')
const createValue = async () => 'value'
await createValue()
Async Iteration (for await...of Loops)
This rule also inspects for await...of statements, and reports if the value being iterated over is not async-iterable.
:::info[Why does the rule report on for await...of loops used on an array of Promises?]
While for await...of can be used with synchronous iterables, and it will await each promise produced by the iterable, it is inadvisable to do so.
There are some tiny nuances that you may want to consider.
The biggest difference between using for await...of and using for...of (apart from awaiting each result yourself) is error handling.
When an error occurs within the loop body, for await...of does not close the original sync iterable, while for...of does.
For detailed examples of this, see the MDN documentation on using for await...of with sync-iterables.
Also consider whether you need sequential awaiting at all. Using for await...of may obscure potential opportunities for concurrent processing, such as those reported by no-await-in-loop. Consider instead using one of the promise concurrency methods for better performance.
Examples
async function syncIterable() {
  const arrayOfValues = [1, 2, 3]
  for await (const value of arrayOfValues) {
    console.log(value)
  }
}
async function syncIterableOfPromises() {
  const arrayOfPromises = [
    Promise.resolve(1),
    Promise.resolve(2),
    Promise.resolve(3),
  ]
  for await (const promisedValue of arrayOfPromises) {
    console.log(promisedValue)
  }
}
async function syncIterable() {
  const arrayOfValues = [1, 2, 3]
  for (const value of arrayOfValues) {
    console.log(value)
  }
}
async function syncIterableOfPromises() {
  const arrayOfPromises = [
    Promise.resolve(1),
    Promise.resolve(2),
    Promise.resolve(3),
  ]
  for (const promisedValue of await Promise.all(arrayOfPromises)) {
    console.log(promisedValue)
  }
}
async function validUseOfForAwaitOnAsyncIterable() {
  async function* yieldThingsAsynchronously() {
    yield 1
    await new Promise((resolve) => setTimeout(resolve, 1000))
    yield 2
  }
  for await (const promisedValue of yieldThingsAsynchronously()) {
    console.log(promisedValue)
  }
}
Explicit Resource Management (await using Statements)
This rule also inspects await using statements.
If the disposable being used is not async-disposable, an await using statement is unnecessary.
Examples
function makeSyncDisposable(): Disposable {
  return {
    [Symbol.dispose](): void {
      // Dispose of the resource
    },
  }
}
async function shouldNotAwait() {
  await using resource = makeSyncDisposable()
}
function makeSyncDisposable(): Disposable {
  return {
    [Symbol.dispose](): void {
      // Dispose of the resource
    },
  }
}
async function shouldNotAwait() {
  using resource = makeSyncDisposable()
}
function makeAsyncDisposable(): AsyncDisposable {
  return {
    async [Symbol.asyncDispose](): Promise<void> {
      // Dispose of the resource asynchronously
    },
  }
}
async function shouldAwait() {
  await using resource = makeAsyncDisposable()
}
When Not To Use It
If you want to allow code to await non-Promise values.
For example, if your framework is in transition from one style of asynchronous code to another, it may be useful to include awaits unnecessarily.
This is generally not preferred but can sometimes be useful for visual consistency.
TypeScript allows specifying a type keyword on imports to indicate that the export exists only in the type system, not at runtime.
This allows transpilers to drop imports without knowing the types of the dependencies.
See Blog > Consistent Type Exports and Imports: Why and How for more details.
Options
prefer
Valid values for prefer are:
- type-importswill enforce that you always use- import type Foo from '...'except referenced by metadata of decorators. It is the default.
- no-type-importswill enforce that you always use- import Foo from '...'.
Examples of correct code with {prefer: 'type-imports'}, and incorrect code with {prefer: 'no-type-imports'}.
import type { Foo } from 'Foo'
import type Bar from 'Bar'
type T = Foo
const x: Bar = 1
Examples of incorrect code with {prefer: 'type-imports'}, and correct code with {prefer: 'no-type-imports'}.
import { Foo } from 'Foo'
import Bar from 'Bar'
type T = Foo
const x: Bar = 1
fixStyle
Valid values for fixStyle are:
- separate-type-importswill add the type keyword after the import keyword- import type { A } from '...'. It is the default.
- inline-type-importswill inline the type keyword- import { type A } from '...'and is only available in TypeScript 4.5 and onwards. See documentation here.
import { Foo } from 'Foo'
import Bar from 'Bar'
type T = Foo
const x: Bar = 1
import type { Foo } from 'Foo'
import type Bar from 'Bar'
type T = Foo
const x: Bar = 1
import { type Foo } from 'Foo'
import type Bar from 'Bar'
type T = Foo
const x: Bar = 1
disallowTypeAnnotations
Examples of incorrect code with {disallowTypeAnnotations: true}:
type T = import('Foo').Foo
const x: import('Bar') = 1
Caveat: @decorators + experimentalDecorators: true + emitDecoratorMetadata: true
:::note
If you are using experimentalDecorators: false (eg TypeScript v5.0's stable decorators) then the rule will always report errors as expected.
This caveat only applies to experimentalDecorators: true
The rule will not report any errors in files that contain decorators when both experimentalDecorators and emitDecoratorMetadata are turned on.
See Blog > Changes to consistent-type-imports when used with legacy decorators and decorator metadata for more details.
If you are using type-aware linting then we will automatically infer your setup from your tsconfig and you should not need to configure anything.
Otherwise you can explicitly tell our tooling to analyze your code as if the compiler option was turned on by setting both parserOptions.emitDecoratorMetadata = true and parserOptions.experimentalDecorators = true.
Comparison with importsNotUsedAsValues / verbatimModuleSyntax
verbatimModuleSyntax was introduced in TypeScript v5.0 (as a replacement for importsNotUsedAsValues).
This rule and verbatimModuleSyntax mostly behave in the same way.
There are a few behavior differences:
| Situation | consistent-type-imports(ESLint) | verbatimModuleSyntax(TypeScript) | 
|---|---|---|
| Unused imports | Ignored (consider using @typescript-eslint/no-unused-vars) | Type error | 
| Usage with emitDecoratorMetadata&experimentalDecorations | Ignores files that contain decorators | Reports on files that contain decorators | 
| Failures detected | Does not fail tscbuild; can be auto-fixed with--fix | Fails tscbuild; cannot be auto-fixed on the command-line | 
| import { type T } from 'T'; | TypeScript will emit nothing (it "elides" the import) | TypeScript emits import {} from 'T' | 
Because there are some differences, using both this rule and verbatimModuleSyntax at the same time can lead to conflicting errors.
As such we recommend that you only ever use one or the other -- never both.
When Not To Use It
If you specifically want to use both import kinds for stylistic reasons, or don't wish to enforce one style over the other, you can avoid this rule.
However, keep in mind that inconsistent style can harm readability in a project. We recommend picking a single option for this rule that works best for your project.
Related To
JavaScript classes may define a constructor method that runs when a class instance is newly created.
TypeScript allows interfaces that describe a static class object to define a new() method (though this is rarely used in real world code).
Developers new to JavaScript classes and/or TypeScript interfaces may sometimes confuse when to use constructor or new.
This rule reports when a class defines a method named new or an interface defines a method named constructor.
Examples
declare class C {
  new(): C
}
interface I {
  new (): I
  constructor(): void
}
declare class C {
  constructor()
}
interface I {
  new (): C
}
When Not To Use It
If you intentionally want a class with a new method, and you're confident nobody working in your code will mistake it with a constructor, you might not want this rule.
This rule forbids providing Promises to logical locations such as if statements in places where the TypeScript compiler allows them but they are not handled properly.
These situations can often arise due to a missing await keyword or just a misunderstanding of the way async
functions are handled/awaited.
:::tip
no-misused-promises only detects code that provides Promises to incorrect logical locations.
See no-floating-promises for detecting unhandled Promise statements.
Options
checksConditionals
If you don't want to check conditionals, you can configure the rule with "checksConditionals": false:
{
  "@typescript-eslint/no-misused-promises": [
    "error",
    {
      "checksConditionals": false
    }
  ]
}
Doing so prevents the rule from looking at code like if (somePromise).
checksVoidReturn
Likewise, if you don't want to check functions that return promises where a void return is expected, your configuration will look like this:
{
  "@typescript-eslint/no-misused-promises": [
    "error",
    {
      "checksVoidReturn": false
    }
  ]
}
You can disable selective parts of the checksVoidReturn option by providing an object that disables specific checks. For example, if you don't mind that passing a () => Promise<void> to a () => void parameter or JSX attribute can lead to a floating unhandled Promise:
{
  "@typescript-eslint/no-misused-promises": [
    "error",
    {
      "checksVoidReturn": {
        "arguments": false,
        "attributes": false
      }
    }
  ]
}
The following sub-options are supported:
arguments
Disables checking an asynchronous function passed as argument where the parameter type expects a function that returns void.
attributes
Disables checking an asynchronous function passed as a JSX attribute expected to be a function that returns void.
inheritedMethods
Disables checking an asynchronous method in a type that extends or implements another type expecting that method to return void.
:::note
For now, no-misused-promises only checks named methods against extended/implemented types: that is, call/construct/index signatures are ignored. Call signatures are not required in TypeScript to be consistent with one another, and construct signatures cannot be async in the first place. Index signature checking may be implemented in the future.
properties
Disables checking an asynchronous function passed as an object property expected to be a function that returns void.
returns
Disables checking an asynchronous function returned in a function whose return type is a function that returns void.
variables
Disables checking an asynchronous function used as a variable whose return type is a function that returns void.
checksSpreads
If you don't want to check object spreads, you can add this configuration:
{
  "@typescript-eslint/no-misused-promises": [
    "error",
    {
      "checksSpreads": false
    }
  ]
}
Examples
checksConditionals
Examples of code for this rule with checksConditionals: true:
const promise = Promise.resolve('value')
if (promise) {
  // Do something
}
const val = promise ? 123 : 456
;[1, 2, 3].filter(() => promise)
while (promise) {
  // Do something
}
const promise = Promise.resolve('value')
// Always `await` the Promise in a conditional
if (await promise) {
  // Do something
}
const val = (await promise) ? 123 : 456
const returnVal = await promise
;[1, 2, 3].filter(() => returnVal)
while (await promise) {
  // Do something
}
checksVoidReturn
Examples of code for this rule with checksVoidReturn: true:
;[1, 2, 3].forEach(async (value) => {
  await fetch(`/${value}`)
})
new Promise<void>(async (resolve, reject) => {
  await fetch('/')
  resolve()
})
document.addEventListener('click', async () => {
  console.log('synchronous call')
  await fetch('/')
  console.log('synchronous call')
})
interface MySyncInterface {
  setThing(): void
}
class MyClass implements MySyncInterface {
  async setThing(): Promise<void> {
    this.thing = await fetchThing()
  }
}
// for-of puts `await` in outer context
for (const value of [1, 2, 3]) {
  await doSomething(value)
}
// If outer context is not `async`, handle error explicitly
Promise.all(
  [1, 2, 3].map(async (value) => {
    await doSomething(value)
  }),
).catch(handleError)
// Use an async IIFE wrapper
new Promise((resolve, reject) => {
  // combine with `void` keyword to tell `no-floating-promises` rule to ignore unhandled rejection
  void (async () => {
    await doSomething()
    resolve()
  })()
})
// Name the async wrapper to call it later
document.addEventListener('click', () => {
  const handler = async () => {
    await doSomething()
    otherSynchronousCall()
  }
  try {
    synchronousCall()
  } catch (err) {
    handleSpecificError(err)
  }
  handler().catch(handleError)
})
interface MyAsyncInterface {
  setThing(): Promise<void>
}
class MyClass implements MyAsyncInterface {
  async setThing(): Promise<void> {
    this.thing = await fetchThing()
  }
}
checksSpreads
Examples of code for this rule with checksSpreads: true:
const getData = () => fetch('/')
console.log({ foo: 42, ...getData() })
const awaitData = async () => {
  await fetch('/')
}
console.log({ foo: 42, ...awaitData() })
const getData = () => fetch('/')
console.log({ foo: 42, ...(await getData()) })
const awaitData = async () => {
  await fetch('/')
}
console.log({ foo: 42, ...(await awaitData()) })
When Not To Use It
This rule can be difficult to enable on large existing projects that set up many misused Promises. Alternately, if you're not worried about crashes from floating or misused Promises -such as if you have global unhandled Promise handlers registered- then in some cases it may be safe to not use this rule.
Further Reading
Related To
The ?? nullish coalescing runtime operator allows providing a default value when dealing with null or undefined.
Using a ! non-null assertion type operator in the left operand of a nullish coalescing operator is redundant, and likely a sign of programmer error or confusion over the two operators.
Examples
foo! ?? bar
foo.bazz! ?? bar
foo!.bazz! ?? bar
foo()! ?? bar
let x!: string
x! ?? ''
let x: string
x = foo()
x! ?? ''
foo ?? bar
foo ?? bar!
foo!.bazz ?? bar
foo!.bazz ?? bar!
foo() ?? bar
// This is considered correct code because there's no way for the user to satisfy it.
let x: string
x! ?? ''
When Not To Use It
If your project's types don't yet fully describe whether certain values may be nullable, such as if you're transitioning to strictNullChecks, this rule might create many false reports.
Further Reading
It supports TypeScript-specific expressions:
- Marks directives in modules declarations ("use strict", etc.) as not unused
- Marks the following expressions as unused if their wrapped value expressions are unused:
- Assertion expressions: x as number;,x!;,<number>x;
- Instantiation expressions: Set<number>;
 
- Assertion expressions: 
Although the type expressions never have runtime side effects (that is, x!; is the same as x;), they can be used to assert types for testing purposes.
Examples
Set<number>
1 as number
window!
function getSet() {
  return Set
}
// Funtion calls are allowed, so type expressions that wrap function calls are allowed
getSet()<number>
getSet() as Set<unknown>
getSet()!
// Namespaces can have directives
namespace A {
  'use strict'
}
It adds support for TypeScript features, such as types.
Options
FAQs
What benefits does this rule have over TypeScript?
TypeScript provides noUnusedLocals and noUnusedParameters compiler options that can report errors on unused local variables or parameters, respectively.
Those compiler options can be convenient to use if you don't want to set up ESLint and typescript-eslint.
However:
- These lint rules are more configurable than TypeScript's compiler options.
- For example, the varsIgnorePatternoption can customize what names are always allowed to be exempted. TypeScript hardcodes its exemptions to names starting with_. If you would like to emulate the TypeScript style of exempting names starting with_, you can use this configuration (this includes errors as well):{ "rules": { "@typescript-eslint/no-unused-vars": [ "error", { "args": "all", "argsIgnorePattern": "^_", "caughtErrors": "all", "caughtErrorsIgnorePattern": "^_", "destructuredArrayIgnorePattern": "^_", "varsIgnorePattern": "^_", "ignoreRestSiblings": true } ] } }
 
- For example, the 
- ESLint can be configured within lines, files, and folders. TypeScript compiler options are linked to their TSConfig file.
- Many projects configure TypeScript's reported errors to block builds more aggressively than ESLint complaints. Blocking builds on unused variables can be inconvenient.
We generally recommend using @typescript-eslint/no-unused-vars to flag unused locals and parameters instead of TypeScript.
:::tip
Editors such as VS Code will still generally "grey out" unused variables even if noUnusedLocals and noUnusedParameters are not enabled in a project.
Also see similar rules provided by ESLint:
Why does this rule report variables used only for types?
This rule does not count type-only uses when determining whether a variable is used. Declaring variables only to use them for types adds code and runtime complexity. The variables are never actually used at runtime. They can be misleading to readers of the code.
For example, if a variable is only used for typeof, this rule will report:
const box = {
  //  ~~~
  //  'box' is assigned a value but only used as a type.
  value: 123,
}
export type Box = typeof box
Instead, it's often cleaner and less code to write out the types directly:
export interface Box {
  value: number
}
For example, if a Zod schema variable is only used for typeof, this rule will report:
import { z } from 'zod'
const schema = z.object({
  //  ~~~~~~
  //  'schema' is assigned a value but only used as a type.
  value: z.number(),
})
export type Box = z.infer<typeof schema>
Instead, it's often cleaner and less code to write out the types directly:
export interface Box {
  value: number
}
If you find yourself writing runtime values only for types, consider refactoring your code to declare types directly.
Why are variables reported as unused despite being referenced by @link in JSDoc?
JSDoc references are not supported by typescript-eslint.
You can use a rule such as jsdoc/no-undefined-types to resolve variables as used in JSDoc comments.
import type { Box } from './Box'
//            ~~~
//            'Box' is defined but never used.
/**
 * @see {@link Box}
 */
export function getBox() {}
There are two common ways to tell TypeScript that a literal value should be interpreted as its literal type (e.g. 2) rather than general primitive type (e.g. number);
- as const: telling TypeScript to infer the literal type automatically
- aswith the literal type: explicitly telling the literal type to TypeScript
as const is generally preferred, as it doesn't require re-typing the literal value.
This rule reports when an as with an explicit literal type can be replaced with an as const.
Examples
let bar: 2 = 2
let foo = <'bar'>'bar'
let foo = { bar: 'baz' as 'baz' }
let foo = 'bar'
let foo = 'bar' as const
let foo: 'bar' = 'bar' as const
let bar = 'bar' as string
let foo = <string>'bar'
let foo = { bar: 'baz' }
When Not To Use It
If you don't care about which style of literals assertions is used in your code, then you will not need this rule.
However, keep in mind that inconsistent style can harm readability in a project. We recommend picking a single option for this rule that works best for your project.
Ensures that each function is only capable of:
- returning a rejected promise, or
- throwing an Error object.
In contrast, non-async, Promise-returning functions are technically capable of either.
Code that handles the results of those functions will often need to handle both cases, which can get complex.
This rule's practice removes a requirement for creating code to handle both cases.
When functions return unions of
Promiseand non-Promisetypes implicitly, it is usually a mistake—this rule flags those cases. If it is intentional, make the return type explicitly to allow the rule to pass.
Examples
Examples of code for this rule
const arrowFunctionReturnsPromise = () => Promise.resolve('value')
function functionReturnsPromise() {
  return Promise.resolve('value')
}
function functionReturnsUnionWithPromiseImplicitly(p: boolean) {
  return p ? 'value' : Promise.resolve('value')
}
const arrowFunctionReturnsPromise = async () => Promise.resolve('value')
async function functionReturnsPromise() {
  return Promise.resolve('value')
}
// An explicit return type that is not Promise means this function cannot be made async, so it is ignored by the rule
function functionReturnsUnionWithPromiseExplicitly(
  p: boolean,
): string | Promise<string> {
  return p ? 'value' : Promise.resolve('value')
}
async function functionReturnsUnionWithPromiseImplicitly(p: boolean) {
  return p ? 'value' : Promise.resolve('value')
}
Options
allowAny
If you want additional safety, consider turning this option off, as it makes the rule less able to catch incorrect Promise behaviors.
Examples of code with { "allowAny": false }:
const returnsAny = () => ({}) as any
const returnsAny = async () => ({}) as any
allowedPromiseNames
For projects that use constructs other than the global built-in Promise for asynchronous code.
This option allows specifying string names of classes or interfaces that cause a function to be checked as well.
Examples of code with { "allowedPromiseNames": ["Bluebird"] }:
class Bluebird {}
const returnsBluebird = () => new Bluebird(() => {})
class Bluebird {}
const returnsBluebird = async () => new Bluebird(() => {})
checkArrowFunctions
checkFunctionDeclarations
checkFunctionExpressions
checkMethodDeclarations
When Not To Use It
This rule can be difficult to enable on projects that use APIs which require functions to always be async.
