References
-
1.1 If you must reassign references, use
let
instead ofvar
. eslint:no-var
Why?
let
is block-scoped rather than function-scoped likevar
.// bad var count = 1; if (true) { count += 1; } // good, use the let. let count = 1; if (true) { count += 1; }
Objects
-
2.1 Use the literal syntax for object creation. eslint:
no-new-object
// bad const item = new Object(); // good const item = {};
-
2.2 Only quote properties that are invalid identifiers. eslint:
quote-props
Why? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines.
// bad const bad = { 'foo': 3, 'bar': 4, 'data-blah': 5, }; // good const good = { foo: 3, bar: 4, 'data-blah': 5, };
Arrays
-
3.1 Use the literal syntax for array creation. eslint:
no-array-constructor
// bad const items = new Array(); // good const items = [];
Strings
-
4.1 Use single quotes
''
for strings. eslint:quotes
// bad const name = "Capt. Janeway"; // bad - template literals should contain interpolation or newlines const name = `Capt. Janeway`; // good const name = 'Capt. Janeway';
Functions
-
5.1 Always put default parameters last. eslint:
default-param-last
// bad function handleThings(opts = {}, name) { // ... } // good function handleThings(name, opts = {}) { // ... }
-
5.2 Avoid confusing arrow function syntax (
=>
) with comparison operators (<=
,>=
). eslint:no-confusing-arrow
// bad const itemHeight = (item) => item.height <= 256 ? item.largeSize : item.smallSize; // bad const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize; // good const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize); // good const itemHeight = (item) => { const { height, largeSize, smallSize } = item; return height <= 256 ? largeSize : smallSize; };
Arrow Functions
-
6.1 Always include parentheses around arguments for clarity and consistency. eslint:
arrow-parens
Why? Minimizes diff churn when adding or removing arguments.
// bad [1, 2, 3].map(x => x * x); // good [1, 2, 3].map((x) => x * x); // bad [1, 2, 3].map(number => ( `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` )); // good [1, 2, 3].map((number) => ( `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` )); // bad [1, 2, 3].map(x => { const y = x + 1; return x * y; }); // good [1, 2, 3].map((x) => { const y = x + 1; return x * y; });
-
6.2 Enforce the location of arrow function bodies with implicit returns. eslint:
implicit-arrow-linebreak
// bad (foo) => bar; (foo) => (bar); // good (foo) => bar; (foo) => (bar); (foo) => ( bar )
Modules
-
7.1 Only import from a path in one place. eslint:
no-duplicate-imports
Why? Having multiple lines that import from the same path can make code harder to maintain.
// bad import foo from 'foo'; // … some other imports … // import { named1, named2 } from 'foo'; // good import foo, { named1, named2 } from 'foo'; // good import foo, { named1, named2, } from 'foo';
-
7.2 Put all
import
s above non-import statements. eslint:import/first
Why? Since
import
s are hoisted, keeping them all at the top prevents surprising behavior.// bad import foo from 'foo'; foo.init(); import bar from 'bar'; // good import foo from 'foo'; import bar from 'bar'; foo.init();
-
7.3 Multiline imports should be indented just like multiline array and object literals. eslint:
object-curly-newline
Why? The curly braces follow the same indentation rules as every other curly brace block in the style guide, as do the trailing commas.
// bad import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; // good import { longNameA, longNameB, longNameC, longNameD, longNameE, } from 'path';
Properties
-
8.1 Use dot notation when accessing properties. eslint:
dot-notation
const luke = { jedi: true, age: 28, }; // bad const isJedi = luke['jedi']; // good const isJedi = luke.jedi;
Variables
-
9.1 Always use
const
orlet
to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. eslint:no-undef
prefer-const
// bad superPower = new SuperPower(); // good const superPower = new SuperPower();
-
9.2 Use one
const
orlet
declaration per variable or assignment. eslint:one-var
Why? It’s easier to add new variable declarations this way, and you never have to worry about swapping out a
;
for a,
or introducing punctuation-only diffs. You can also step through each declaration with the debugger, instead of jumping through all of them at once.// bad const items = getItems(), goSportsTeam = true, dragonball = "z"; // bad // (compare to above, and try to spot the mistake) const items = getItems(), goSportsTeam = true; dragonball = "z"; // good const items = getItems(); const goSportsTeam = true; const dragonball = "z";
-
9.3 Disallow unused variables. eslint:
no-unused-vars
Why? Variables that are declared and not used anywhere in the code are most likely an error due to incomplete refactoring. Such variables take up space in the code and can lead to confusion by readers.
// bad const some_unused_var = 42; // 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; // Unused function arguments. function getX(x, y) { return x; } // good function getXPlusY(x, y) { return x + y; } const x = 1; const y = a + 2; alert(getXPlusY(x, y)); // 'type' is ignored even if unused because it has a rest property sibling. // This is a form of extracting an object that omits the specified keys. const { type, ...coords } = data; // 'coords' is now the 'data' object without its 'type' property.
-
9.4 Avoid using unary increments and decrements (
++
,--
). eslintno-plusplus
Why? Per the eslint documentation, unary increment and decrement statements are subject to automatic semicolon insertion and can cause silent errors with incrementing or decrementing values within an application. It is also more expressive to mutate your values with statements like
num += 1
instead ofnum++
ornum ++
. Disallowing unary increment and decrement statements also prevents you from pre-incrementing/pre-decrementing values unintentionally which can also cause unexpected behavior in your programs.// bad const array = [1, 2, 3]; let num = 1; num++; --num; let sum = 0; let truthyCount = 0; for (let i = 0; i < array.length; i++) { let value = array[i]; sum += value; if (value) { truthyCount++; } } // good const array = [1, 2, 3]; let num = 1; num += 1; num -= 1; const sum = array.reduce((a, b) => a + b, 0); const truthyCount = array.filter(Boolean).length;
Comparison Operators & Equality
-
[10.2]](#comparison--switch-blocks) Use braces to create blocks in
case
anddefault
clauses that contain lexical declarations (e.g.let
,const
,function
, andclass
). eslint:no-case-declarations
Why? Lexical declarations are visible in the entire
switch
block but only get initialized when assigned, which only happens when itscase
is reached. This causes problems when multiplecase
clauses attempt to define the same thing.// bad switch (foo) { case 1: let x = 1; break; case 2: const y = 2; break; case 3: function f() { // ... } break; default: class C {} } // good switch (foo) { case 1: { let x = 1; break; } case 2: { const y = 2; break; } case 3: { function f() { // ... } break; } case 4: bar(); break; default: { class C {} } }