enforce getter and setter pairs in objects and classes
["error", {
"setWithoutGet": true,
"getWithoutSet": false
}]
const foo = {
set bar(value) {
this.barValue = 'bar ' + value;
},
};
const foo = {
set bar(value) {
this.barValue = 'bar ' + value;
},
get bar() {
return this.barValue;
},
};
const bar = {
get foo() {
return this.fooValue;
},
};
const foo = [1, 2, 3].map((num) => {
console.log(num * num);
});
const foo = [1, 2, 3].map((num) => {
return num * num;
});
enforce a maximum cyclomatic complexity allowed in a program
https://en.wikipedia.org/wiki/Cyclomatic_complexity
["error", {
"max": 20
}]
function foo() {
if (i === 1) console.log(i);
if (i === 2) console.log(i);
if (i === 3) console.log(i);
if (i === 4) console.log(i);
if (i === 5) console.log(i);
if (i === 6) console.log(i);
if (i === 7) console.log(i);
if (i === 8) console.log(i);
if (i === 9) console.log(i);
if (i === 10) console.log(i);
if (i === 11) console.log(i);
if (i === 12) console.log(i);
if (i === 13) console.log(i);
if (i === 14) console.log(i);
if (i === 15) console.log(i);
if (i === 16) console.log(i);
if (i === 17) console.log(i);
if (i === 18) console.log(i);
if (i === 19) console.log(i);
if (i === 20) console.log(i);
}
function foo() {
if (i === 1) console.log(i);
if (i === 2) console.log(i);
if (i === 3) console.log(i);
if (i === 4) console.log(i);
if (i === 5) console.log(i);
if (i === 6) console.log(i);
if (i === 7) console.log(i);
if (i === 8) console.log(i);
if (i === 9) console.log(i);
if (i === 10) console.log(i);
bar(i);
}
function bar(i) {
if (i === 11) console.log(i);
if (i === 12) console.log(i);
if (i === 13) console.log(i);
if (i === 14) console.log(i);
if (i === 15) console.log(i);
if (i === 16) console.log(i);
if (i === 17) console.log(i);
if (i === 18) console.log(i);
if (i === 19) console.log(i);
if (i === 20) console.log(i);
}
require return
statements to either always or never specify values
缺少 TypeScript 的支持,类型判断是不准确的
class Foo extends Bar {
constructor() {}
}
class Foo extends Bar {
constructor() {
super();
}
}
switch (foo) {
default:
bar();
break;
case 1:
baz();
break;
}
switch (foo) {
case 1:
baz();
break;
default:
bar();
break;
}
if (foo == 1) {
}
if (bar != null) {
}
if (foo === 1) {
}
if (bar !== null) {
}
for (let i = 0; i < 10; i--) {
// do something
}
for (let i = 0; i < 10; i++) {
// do something
}
require function names to match the name of the variable or property to which they are assigned
["error", "always"]
const foo = function bar() {};
const foo = function () {};
const bar = function bar() {};
const user = {
get name() {
// do something
},
};
class User {
get name() {
return;
}
}
const user = {
get name() {
return 'Alex';
},
};
class User {
get name() {
return this.name;
}
}
const foo = {
set bar(value) {
this.barValue = 'bar ' + value;
},
baz: 1,
get bar() {
return this.barValue;
},
};
const foo = {
set bar(value) {
this.barValue = 'bar ' + value;
},
get bar() {
return this.barValue;
},
baz: 1,
};
for (key in foo) {
doSomething(key);
}
for (key in foo) {
if (Object.prototype.hasOwnProperty.call(foo, key)) {
doSomething(key);
}
}
require or disallow an empty line between class members
有时为了紧凑需要挨在一起,有时为了可读性需要空一行
function foo() {
if (true) {
if (true) {
if (true) {
if (true) {
if (true) {
if (true) {
}
}
}
}
}
}
}
function foo() {
if (true) {
if (true) {
if (true) {
if (true) {
if (true) {
}
}
}
}
}
}
foo(() => {
bar(() => {
baz(() => {
qux(() => {});
});
});
});
foo(async () => {
await bar();
await baz();
await qux();
});
function foo(a1, a2, a3, a4) {}
function foo(a1, a2, a3) {}
function bar({ a1, a2, a3, a4 }) {}
require constructor names to begin with a capital letter
["error", {
"newIsCap": true,
"capIsNew": false,
"properties": true
}]
new foo();
new foo.bar();
new Foo();
new foo.Bar();
Foo();
disallow Array
constructors
参数为一个时表示创建一个指定长度的数组,比较常用
参数为多个时表示创建一个指定内容的数组,此时可以用数组字面量实现,不必使用构造函数
const foo = Array(0, 1, 2); // [0, 1, 2]
const bar = new Array(0, 1, 2); // [0, 1, 2]
const foo = [0, 1, 2];
Array(3); // [empty × 3]
new Array(3); // [empty × 3]
Array(3).fill('foo'); // ["foo", "foo", "foo"]
new Array(3).fill('foo'); // ["foo", "foo", "foo"]
disallow using an async function as a Promise executor
出现这种情况时,一般不需要使用 new Promise 实现异步了
new Promise(async (resolve) => {
setTimeout(resolve, 1000);
});
new Promise((resolve) => {
setTimeout(resolve, 1000);
});
function foo(n) {
if (n <= 0) {
return;
}
arguments.callee(n - 1);
}
function foo(n) {
if (n <= 0) {
return;
}
foo(n - 1);
}
switch (foo) {
case 1:
const x = 1;
break;
}
switch (foo) {
case 1: {
const x = 1;
break;
}
}
disallow assignment operators in conditional expressions
["error", "except-parens"]
if (foo = 0) {
}
if (foo === 0) {
}
if (bar === (foo = 0)) {
}
const foo = 1;
foo = 2;
let foo = 1;
foo = 2;
for (const bar in [1, 2, 3]) {
console.log(bar);
}
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 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;
disallow constant expressions in conditions
["error", {
"checkLoops": false
}]
if (true) {
}
const foo = 0 ? 'bar' : 'baz';
for (; true; ) {
if (foo === 0) break;
}
while (true) {
if (foo === 0) break;
}
class Foo {
constructor(bar) {
this.bar = bar;
return bar;
}
}
class Foo {
constructor(bar) {
if (!bar) {
return;
}
this.bar = bar;
}
}
disallow division operators explicitly at the beginning of regular expressions
有代码高亮的话,在阅读这种代码时,也完全不会产生歧义或理解上的困难
class Foo {
bar() {}
bar() {}
}
class Foo {
bar() {}
baz() {}
}
if (foo) {
console.log(foo);
} else if (foo) {
console.log(bar);
}
if (foo) {
console.log(foo);
} else if (bar) {
console.log(bar);
}
const foo = {
bar: 1,
bar: 2,
};
const foo = {
bar: 1,
baz: 2,
};
switch (foo) {
case 1:
break;
case 2:
break;
case 1:
break;
}
switch (foo) {
case 1:
break;
case 2:
break;
case 3:
break;
}
disallow else
blocks after return
statements in if
statements
else 中使用 return 可以使代码结构更清晰
if (foo) {
}
if (foo) {
// do something
}
try {
// do something
} catch (e) {}
const reg = /abc[]/;
const reg = /abc[a-z]/;
if (foo == null) {
}
if (foo === null) {
}
try {
} catch (e) {
e = 10;
}
try {
} catch (e) {
console.error(e);
}
Array.prototype.flat = function () {
// do something
};
[1, [2, 3]].flat();
function flat(arr) {
// do something
}
flat([1, [2, 3]]);
(function () {
foo();
}).bind(bar);
(function () {
this.foo();
}).bind(bar);
if (!!foo) {
}
if (Boolean(foo)) {
}
if (foo) {
}
if (!foo) {
}
switch (foo) {
case 1:
doSomething();
case 2:
doSomethingElse();
}
switch (foo) {
case 1:
doSomething();
break;
case 2:
doSomethingElse();
}
switch (foo) {
case 1:
case 2:
doSomething();
}
function foo() {}
foo = 1;
let foo = function () {};
foo = 1;
Object = null;
foo = null;
const b = ~foo.indexOf('.');
const n = +foo;
const m = 1 * foo;
const s = '' + foo;
foo += '';
const b = foo.indexOf('.') !== -1;
const n = Number(foo);
const m = Number(foo);
const s = String(foo);
foo = String(foo);
const c = !!foo;
disallow variable and function
declarations in the global scope
模块化之后,不会出现这种在全局作用域下定义变量的情况
setTimeout('alert("Hello World");', 1000);
setTimeout(() => {
alert('Hello World');
}, 1000);
import foo from 'foo';
foo = 1;
import * as bar from 'bar';
bar.baz = 1;
import foo from 'foo';
foo.baz = 1;
import * as bar from 'bar';
bar.baz.qux = 1;
disallow variable or function
declarations in nested blocks
["error", "both"]
if (foo) {
function bar() {}
}
if (foo) {
const bar = function () {};
}
const reg1 = new RegExp('[');
const reg2 = new RegExp('.', 'z');
const reg1 = new RegExp('[a-z]');
const reg2 = new RegExp('.', 'g');
function foo() {
this.a = 0;
}
class Foo {
constructor() {
this.a = 0;
}
}
disallow irregular whitespace
["error", {
"skipStrings": true,
"skipComments": false,
"skipRegExps": true,
"skipTemplates": true
}]
function foo() {
}
const foo = ' ';
const bar = / /;
const baz = ` `;
disallow the use of the __iterator__
property
__iterator__ 是一个已废弃的属性
使用 [Symbol.iterator] 替代它
Foo.prototype.__iterator__ = function () {
return new FooIterator(this);
};
let foo = {};
foo[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
console.log([...foo]);
// [1, 2, 3]
loop:
for (let i = 0; i < 5; i++) {
if (i === 1) {
continue loop;
}
console.log(i);
}
// 0 2 3 4
for (let i = 0; i < 5; i++) {
if (i === 1) {
continue;
}
console.log(i);
}
// 0 2 3 4
disallow function declarations that contain unsafe references inside loop statements
使用 let 就已经解决了这个问题了
const foo = 5123000000000000000000000000001;
const foo = 12345;
disallow characters which are made with multiple code points in character class syntax
某些特殊字符很难看出差异,最好不要在正则中使用
/^[Á]$/u.test('Á'); // false
/^[A]$/u.test('A'); // true
new Foo();
const foo = new foo();
const foo = new Function('a', 'b', 'return a + b');
const foo = function (a, b) {
return a + b;
};
const foo = new Symbol('foo');
const bar = new BigInt(9007199254740991);
const foo = Symbol('foo');
const bar = BigInt(9007199254740991);
const foo = new Symbol('foo');
const foo = Symbol('foo');
const s = new String('foo');
const n = new Number(1);
const b = new Boolean(true);
const s = String(someValue);
const n = Number(someValue);
const b = Boolean(someValue);
Disallow \8
and \9
escape sequences in string literals
代码格式问题,最好由 Prettier 解决
const foo = Math();
const bar = JSON();
const baz = Reflect();
const foo = Math.random();
const bar = JSON.parse('{}');
const baz = Reflect.get({ x: 1, y: 2 }, 'x');
function foo(bar) {
bar = bar || '';
}
function foo(bar_) {
bar = bar_ || '';
}
new Promise((resolve, reject) => {
if (someCondition) {
return defaultResult;
}
getSomething((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
new Promise((resolve, reject) => {
if (someCondition) {
resolve(defaultResult);
return;
}
getSomething((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
const foo = bar.__proto__;
bar.__proto__ = baz;
const foo = Object.getPrototypeOf(bar);
Object.setPrototypeOf(bar, baz);
disallow calling some Object.prototype
methods directly on objects
hasOwnProperty 比较常用
const reg1 = /foo bar/;
const reg2 = new RegExp('foo bar');
const reg1 = /foo {3}bar/;
const reg2 = new RegExp('foo {3}bar');
function foo() {
return (bar = 1);
}
function foo() {
bar = 1;
return bar;
}
if (foo === foo) {
}
if (NaN === NaN) {
}
if (foo === bar) {
}
if (isNaN(foo)) {
}
const foo = {
set bar(value) {
this.barValue = 'bar ' + value;
return this.barValue;
},
};
const foo = {
set bar(value) {
this.barValue = 'bar ' + value;
},
};
const undefined = 1;
function foo(NaN) {}
function Infinity() {}
console.log(undefined);
console.log(NaN);
console.log(Infinity);
const foo = 'Hello ${bar}';
const foo = 'Hello {bar}';
class Foo extends Bar {
constructor() {
this.foo = 1;
super();
}
}
class Foo extends Bar {
constructor() {
super();
this.foo = 1;
}
}
throw 'foo';
throw 1;
throw new Error('foo');
foo(bar);
function foo() {}
const bar = 1;
foo(bar);
if (typeof baz === 'number') {
}
let foo = 10;
while (foo) {
console.log(foo);
}
let foo = 10;
while (foo) {
console.log(foo);
foo--;
}
function foo() {
return;
const bar = 1;
}
function foo() {
return;
// const bar = 1;
}
for (foo of bar) {
if (foo.id === id) {
doSomething(foo);
}
break;
}
for (foo of bar) {
if (foo.id === id) {
doSomething(foo);
break;
}
}
function foo() {
try {
return 1;
} finally {
// finally 会在 try 之前执行,故会 return 2
return 2;
}
}
function foo() {
try {
return 1;
} finally {
console.log(2);
}
}
if (!key in object) {
}
if (!obj instanceof SomeClass) {
}
if (!(key in object)) {
}
if (!(obj instanceof SomeClass)) {
}
disallow use of optional chaining in contexts where the undefined
value is not allowed
(obj?.foo)();
(obj?.foo ?? obj?.bar)();
obj?.foo();
(obj?.foo ?? bar)();
Disallow unused expressions
["error", {
"allowShortCircuit": true,
"allowTernary": true,
"allowTaggedTemplates": true
}]
1;
foo;
('foo');
foo && bar;
foo || bar;
foo ? bar : baz;
`bar`;
'use strict';
foo && bar();
foo || bar();
foo ? bar() : baz();
foo`bar`;
class Foo1 {
#unusedMember = 5;
}
class Foo2 {
#usedOnlyInWrite = 5;
method() {
this.#usedOnlyInWrite = 42;
}
}
class Foo3 {
#usedOnlyToUpdateItself = 5;
method() {
this.#usedOnlyToUpdateItself++;
}
}
class Foo4 {
#unusedMethod() {}
}
class Foo5 {
get #unusedAccessor() {}
set #unusedAccessor(value) {}
}
class Foo1 {
#usedMember = 42;
method() {
return this.#usedMember;
}
}
class Foo2 {
#usedMethod() {
return 42;
}
anotherMethod() {
return this.#usedMethod();
}
}
class Foo3 {
get #usedAccessor() {}
set #usedAccessor(value) {}
method() {
this.#usedAccessor = 42;
}
}
disallow unused variables
["error", {
"vars": "all",
"args": "none",
"ignoreRestSiblings": false,
"caughtErrors": "none"
}]
let foo = 1;
foo = 2;
function bar(baz) {}
const { baz, ...rest } = data;
let foo = 1;
console.log(foo);
function bar(baz) {}
bar();
const { baz, ...rest } = data;
console.log(baz, rest);
try {
} catch (e) {}
disallow the use of variables before they are defined
["error", {
"variables": false,
"functions": false,
"classes": false
}]
console.log(foo);
const foo = 1;
new Baz();
class Baz {}
(() => {
console.log(foo);
})();
const foo = 1;
console.log(foo);
bar();
function bar() {}
(() => {
new Baz();
})();
class Baz {}
new Baz();
Disallow useless backreferences in regular expressions
某些回溯引用语法上没问题,但是会永远匹配到空字符串
/^(?:(a)|\1b)$/; // reference to (a) into another alternative
/^(?:(a)|(b)\2)$/; // reference to (b)
foo.call(null, 1, 2, 3); // foo(1, 2, 3)
foo.apply(null, [1, 2, 3]); // foo(1, 2, 3)
foo.bar.call(foo, 1, 2, 3); // foo.bar(1, 2, 3);
foo.bar.apply(foo, [1, 2, 3]); // foo.bar(1, 2, 3);
foo.call(bar, 1, 2, 3);
foo.apply(bar, [1, 2, 3]);
foo.bar.call(baz, 1, 2, 3);
foo.bar.apply(baz, [1, 2, 3]);
try {
doSomethingThatMightThrow();
} catch (e) {
throw e;
}
doSomethingThatMightThrow();
try {
doSomethingThatMightThrow();
} catch (e) {
doSomethingBeforeRethrow();
throw e;
}
const foo = {
['1']: 1,
['bar']: 'bar',
};
const foo = {
1: 1,
bar: 'bar',
};
const foo = 'f' + 'oo';
const bar = `b` + `ar`;
const foo = 'fo';
const bar = 1 + `ar`;
class Foo {
constructor() {}
}
class Bar extends Foo {
constructor(...args) {
super(...args);
}
}
class Foo {
constructor() {
doSomething();
}
}
class Bar extends Foo {
constructor(...args) {
super(...args);
doSomething();
}
}
import { foo as foo } from 'foo';
const bar = 1;
export { bar as bar };
let { baz: baz } = foo;
import { foo } from 'foo';
const bar = 1;
export { bar };
let { baz } = foo;
require or disallow method and property shorthand syntax for object literals
有时后者可以使代码结构更清晰
enforce variables to be declared either together or separately in functions
["error", "never"]
let foo, bar;
const baz = 1,
qux = 2;
let foo;
let bar;
const baz = 1;
const qux = 2;
foo(function (a) {
return a;
});
oo((a) => a);
enforce using named capture group in regular expression
正则表达式已经较难理解了,没必要强制加上命名组
disallow parseInt()
and Number.parseInt()
in favor of binary, octal, and hexadecimal literals
disallow use of Object.prototype.hasOwnProperty.call()
and prefer use of Object.hasOwn()
ES2022 的新接口,兼容性不太好
disallow using Object.assign with an object literal as the first argument and prefer the use of object spread instead.
const foo = Object.assign({}, bar);
const foo = { ...bar };
// 第一个参数为变量时允许使用 Object.assign
Object.assign(foo, baz);
Promise.reject('foo');
new Promise((resolve, reject) => {
reject();
});
new Promise((resolve, reject) => {
reject('foo');
});
Promise.reject(new Error('foo'));
new Promise((resolve, reject) => {
reject(new Error('foo'));
});
disallow use of the RegExp
constructor in favor of regular expression literals
new RegExp('abc');
new RegExp('\\.', 'g');
/abc/;
/\./g;
new RegExp(prefix + 'abc');
const foo = parseInt('071'); // 57
const foo = parseInt('071', 10); // 71
disallow assignments that can lead to race conditions due to usage of await
or yield
这样会导致不符合预期的结果
https://github.com/eslint/eslint/issues/11899
在上面 issue 修复之前,关闭此规则
function* foo() {
return 1;
}
function* foo() {
yield 1;
return 2;
}
enforce consistent spacing after the //
or /*
in a comment
["error", "always"]
//foo
/*bar */
/**baz */
// foo
/* bar */
/** baz */
'use strict';
function foo() {
'use strict';
}
function foo() {}
if (typeof foo === 'numbe') {
}
if (typeof foo === 'number') {
}