# Introduction

This JavaScript Coding Guide was created to scope what can be done with JavaScript to promote a functional codebase. High-level programming language like JavaScript can benefits more from the maintainability of the functional style than an imperative performance focused style.

To help you understand the main functional programming concepts aborded in this Guide, the Functionnal Programming in JavaScript guide was created as a reference to cover most of the popular subjects with clarity.

This Coding Guide aims not to restrict the style of individual contributors but set the common ground for solving problems as a team with the JavaScript language. This Guide is opinionated, it is not the ideal or better way of programming in JavaScript nor programming Functional JavaScript, it is an idea of how it may be done.

# Formatting

With few exceptions, this Guide does not cover formatting rules. Use Prettier for the code formatting. Prettier is an opinionated code formatter that support many languages and work with most of the code editors.

# Naming

# 1.1 ● Avoid single letter names. Be descriptive with your naming.

Names are for readability, not to appease a computer algorithm.

// bad
const q = () => {
  // ...
};

// good
const query = () => {
  // ...
};

# 1.2 ● Do not use trailing or leading underscores.

It is sometimes intended to indicate privacy, but it does not actually provide privacy. If privacy is important, use closure.

// bad
const __firstName__ = "Panda";
const firstName_ = "Panda";
const _firstName = "Panda";

// good
const firstName = "Panda";

# 1.3 ● Use camelCase when naming objects, functions and instances.

// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
const c = () => {};

// good
const thisIsMyObject = {};
const thisIsMyFunction = () => {};

# 1.4 ● Use PascalCase when naming a constructor, class, function library or bare object.

// bad
class user {
  // ...
}

// good
class User {
  // ...
}

// bad
const defaultSettings = {
  foo: {},
  bar: true,
};

// good
const DefaultSettings = {
  foo: {},
  bar: true,
};

# 1.5 ● Acronyms and initialisms should always be all UPPERCASED.

Also, if you can make them disappear without affecting the meaning, you should do it.

// bad
import SmsContainer from "./containers/sms-container";

// bad
const HttpRequests = [
  // ...
];

// good
import SMSContainer from "./containers/sms-container";

// good
const HTTPRequests = [
  // ...
];

// best - agnostic naming
import textMessageContainer from "./containers/text-message-container";

// best - agnostic naming
const requests = [
  // ...
];

# 1.6 ● Constant should be UPPERCASED if it (1) is exported, (2) it can not be reassigned, and (3) the programmer can trust it and its nested properties to never change.

This is an additional tool to assist in situations where the programmer would be unsure if a variable might ever change. UPPERCASE_VARIABLES are letting the programmer know that they can trust the variable (and its properties) not to change.

  • What about all const variables? This is unnecessary, so uppercasing should not be used for constants within a file. It must be used for exported constants however.
  • What about exported objects? Uppercase at the top level of export (e.g. EXPORTED_OBJECT.key) and maintain that all nested properties do not change.
// bad
const PRIVATE_VARIABLE = "should not be unnecessarily uppercased within a file";

// bad
export const THING_TO_BE_CHANGED = "should obviously not be uppercased";

// bad
export let REASSIGNABLE_VARIABLE = "do not use let with uppercase variables";

// ---

// bad
export const apiKey = "SOMEKEY";

// good
export const API_KEY = "SOMEKEY";

// ---

// bad - unnecessarily uppercases key while adding no semantic value
export const MAPPING = {
  KEY: "value",
};

// good
export const MAPPING = {
  key: "value",
};

# 1.7 ● Accessors with a boolean return should be prefixed with "is" or "has" according to the subject.

const users = [
  {
    firstName: "John",
    permissions: ["admin"],
  },
];

// bad
const firstName = (user) => !!user.firstName;
const isFirstName = (user) => !!user.firstName;

// good
const hasFirstName = (user) => !!user.firstName;

// bad
const admin = (user) => user.permissions.includes["admin"];
const hasAdmin = (user) => user.permissions.includes["admin"];

// good
const isAdmin = (user) => user.permissions.includes["admin"];

# Declarations & Variables

# 2.1 ● Always use const or let to declare variables.

Not doing so will result in global variables. We want to avoid polluting the global namespace.

// bad
now = Date.now();

// good
const now = Date.now();

# 2.2 ● Prefer const for all of your references. You must never use var.

This ensures that you can't reassign your references, which can lead to bugs and difficult to comprehend code.

// bad
var a = 1;
var b = 2;

// good
const a = 1;
const b = 2;

# 2.3 ● If you must reassign references, you should use let. You must avoid using it at all cost.

// bad
var count = 1;
// ...
someCondition && count += 1;

// good
let count = 1;
// ...
someCondition && count += 1;

# 2.4 ● Use one declaration per variable or assignment.

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(),
  isSportsTeam = true,
  dragonball = "z";

// bad
// (compare to above, and try to spot the mistake)
const items = getItems(),
  isSportsTeam = true;
dragonball = "z";

// good
const items = getItems();
const isSportsTeam = true;
const dragonball = "z";

# 2.5 ● Do not chain variable assignments.

Chaining variable assignments creates implicit global variables.

// bad
const a = (b = c = 1);

// good
const a = 1;
const b = a;
const c = a;

# 2.6 ● Group all your const and then group all your let.

This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables.

// bad
let i;
const items = getItems();
let dragonball;
const isSportsTeam = true;
let len;

// good
const isSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;

# 2.7 ● Avoid unused variables.

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.

# Strings & Regex

# 3.1 ● Use String for type casting.

const reviewScore = 9;

// bad
const totalScore = new String(reviewScore); // typeof totalScore is "object" not "string"

// bad
const totalScore = reviewScore + ""; // invokes reviewScore.valueOf()

// bad
const totalScore = reviewScore.toString(); // isn't guaranteed to return a string

// good
const totalScore = String(reviewScore);

# 3.2 ● Use template strings instead of concatenation.

Template strings give you a readable, concise syntax with proper newlines and string interpolation features.

// bad
const sayHi = (name) => "How are you, " + name + "?";

// bad
const sayHi = (name) => ["How are you, ", name, "?"].join();

// good
const sayHi = (name) => `How are you, ${name}?`;

# 3.3 ● Avoid the new Regex construct.

You should use it as a string literal inside a function that does just that.

// bad
const trim = (str) => str.trim(new Regex(/[ ]+$/)))

// good
const trim = (str) => str.trim(/[ ]+$/)

# Booleans

# 4.1 ● Use !! for type casting.

const age = 0;

// bad
const hasAge = new Boolean(age);

// bad
const hasAge = Boolean(age);

// good
const hasAge = !!age;

# Numbers, Dates & Maths

# 5.1 ● Prefer Number for type casting and parseInt always with a radix for parsing strings.

const inputValue = "4";

// bad
const val = new Number(inputValue);

// bad
const val = +inputValue;

// bad - most of the time
const val = inputValue >> 0;

// bad
const val = parseInt(inputValue);

// good
const val = Number(inputValue);

// good
const val = parseInt(inputValue, 10);

If for whatever reason you are doing something wild and parseInt is your bottleneck and need to use Bitshift for performance reasons, leave a comment explaining why and what you're doing.

// good
/**
 * parseInt was the reason my code was slow.
 * Bitshifting the String to coerce it to a
 * Number made it a lot faster.
 */
const val = inputValue >> 0;

WARNING

Be careful when using bitshift operations. Numbers are represented as 64-bit values, but bitshift operations always return a 32-bit integer (source). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. Largest signed 32-bit Int is 2,147,483,647.

2147483647 >> 0; // => 2147483647
2147483648 >> 0; // => -2147483648
2147483649 >> 0; // => -2147483647

# 5.2 ● Use exponentiation operator ** when calculating exponentiations.

// bad
const binary = Math.pow(2, 10);

// good
const binary = 2 ** 10;

# 5.3 ● Avoid using unary ++ and --.

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 of num++ or num ++. Disallowing unary increment and decrement statements also prevents you from pre-incrementing or pre-decrementing values unintentionally which can also cause unexpected behavior in your programs.

// bad
foo++;
bar--;

// good
foo += 1;
bar += 1;

// best
numbers.map((foo) => foo + 1);
numbers.map((bar) => bar - 1);

# Arrays

# 6.1 ● Use the literal syntax for array creation.

// bad
const items = new Array();

// good
const items = [];

# 6.2 ● Prefer Array.push instead of direct assignment to add items to an array.

const someStack = [];

// bad
someStack[someStack.length] = "abracadabra";

// good
someStack.push("abracadabra");

# 6.3 ● Use array spreads to copy arrays.

// bad
const itemsCopy = [];

items.forEach((item) => itemsCopy.push(item));

// bad
const itemsCopy = items.map((item) => item);

// good
const itemsCopy = [...items];

# 6.4 ● Use array spreads for converting an iterable object to an array.

const foo = document.querySelectorAll(".foo");

// bad
const nodes = Array.from(foo);

// good
const nodes = [...foo];

# 6.5 ● Use Array.from for converting an array-like object to an array.

const arrLike = { 0: "foo", 1: "bar", 2: "baz", length: 3 };

// bad
const arr = Array.prototype.slice.call(arrLike);

// good
const arr = Array.from(arrLike);

# 6.6 ● Do not use Array.from instead of Array.map for mapping over iterables.

// array
const foo = [a, b, c];

// bad
const baz = Array.from(foo, bar);

// good
const baz = foo.map(bar);

// iterable object
const foo = document.querySelectorAll(".foo");

// bad
const baz = Array.from(foo, bar);

// good
const baz = [...foo].map(bar);

# 6.7 ● Array method callbacks must have a returned value.

// bad - use single expression return
[1, 2, 3].map((x) => {
  return x + 1;
});

// good
[1, 2, 3].map((x) => x + 1);

// bad - no returned value means `acc` becomes undefined after the first iteration
[
  [0, 1],
  [2, 3],
  [4, 5],
].reduce((acc, item, index) => {
  const flatten = acc.concat(item);
});

// good
[
  [0, 1],
  [2, 3],
  [4, 5],
].reduce((acc, item, index) => acc.concat(item));

// bad
inbox.filter((msg) => {
  const { subject, author } = msg;
  if (subject === "Mockingbird") {
    return author === "Harper Lee";
  } else {
    return false;
  }
});

// good
inbox.filter(
  ({ subject, author }) => subject === "Mockingbird" && author === "Harper Lee"
);

# Objects

# 7.1 ● Use the literal syntax for object creation.

// bad
const item = new Object();

// good
const item = {};

# 7.2 ● Do not use object method shorthand.

They influence imperative programming and force the usage of return

// bad
const atom = {
  value: 1,
  addValue(value) {
    return atom.value + value;
  },
};

// good
const atom = {
  value: 1,
  addValue: (value) => atom.value + value,
};

# 7.3 ● Use property value shorthand.

It is shorter and descriptive.

const lukeSkywalker = "Luke Skywalker";

// bad
const obj = {
  lukeSkywalker: lukeSkywalker,
};

// good
const obj = {
  lukeSkywalker,
};

# 7.4 ● Do not call Object.prototype methods directly.

Do not call Object.prototype methods directly, such as hasOwnProperty, propertyIsEnumerable, and isPrototypeOf.

These methods may be shadowed by properties on the object in question.

const obj = {
  hasOwnProperty: false,
  propertyIsEnumerable: true,
  isPrototypeOf: () => {},
};

// bad
console.log(obj.hasOwnProperty(key));
console.log(obj.propertyIsEnumerable(key));
console.log(obj.isPrototypeOf(key));

// good
const has = (object) => (key) =>
  Object.prototype.hasOwnProperty.call(object, key);

console.log(has(key)(object));

// best
// Use a standardized FP library
import { has } from "ramda";

console.log(has(key)(object));

# 7.5 ● Prefer the object spread operator over Object.assign to shallow-copy objects. Prefer the object rest operator to get a new object with certain properties omitted.

// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
delete copy.a; // so does this

// bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }

// good
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }

const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

# Properties

# 8.1 ● Use dot notation when accessing properties.

const luke = {
  jedi: true,
  age: 28,
};

// bad
const isJedi = luke["jedi"];

// good
const isJedi = luke.jedi;

# 8.2 ● Use bracket notation when accessing properties with a variable.

const luke = {
  jedi: true,
  age: 28,
};

const getProp = (obj) => (prop) => obj[prop];

const isJedi = getProp(luke)("jedi");

# 8.3 ● Use safe techniques to access properties.

// bad
const baz = foo.bar[5].baz;

// good
// With the logical AND operator
const baz = foo && foo.bar && foo.bar[5] && foo.bar[5].baz;

// good - but sometime can add a mental tax
// With destructuring
const { bar = [] } = foo || {};
const { baz } = bar[5] || {};

// best
// With library
import { path } from "ramda";

const baz = path(["bar", 5, "baz"])(foo);

// bad - for the moment, this operator is not supported
// With the optional chaining operator
const baz = foo?.bar?.[5]?.baz;

# Destructuring

# 9.1 ● Use array destructuring.

const arr = [1, 2, 3, 4];

// bad
const first = arr[0];
const second = arr[1];

// good
const [first, second] = arr;

# 9.2 ● Use object destructuring when accessing and using multiple properties of an object.

Destructuring saves you from creating temporary references for those properties.

// bad
const getFullName = (user) => {
  const firstName = user.firstName;
  const lastName = user.lastName;

  return `${firstName} ${lastName}`;
};

// ok
const getFullName = (user) => {
  const { firstName, lastName } = user;
  return `${firstName} ${lastName}`;
};

// good
const getFullName = ({ firstName, lastName }) => `${firstName} ${lastName}`;

# 9.3 ● Use object destructuring only if the structure is static.

Destructuring binds the function to the json structure. Use it only if the structure is static.

Example with changin structure

// ok
const parse = ({ a: { b } }) =>
  Id(b)
    .map(parseInt)
    .fold((a) => a);

// good
const parse = (path) => (data) =>
  Id(data)
    .map(path)
    .map(parseInt)
    .fold((a) => a);

// best
const parse = (path) => pipe(path, parseInt);

Example with static structure

import { pathOr } from "ramda";

// good
const getHrefFromWindow = (
  { location: { href } } = { location: { href: "" } }
) => href;

// best
const getHrefFromWindow = (window) => pathOr("", ["location", "href"])(window);

# 9.4 ● Use object destructuring for multiple return values, not array destructuring.

You can add new properties over time or change the order of things without breaking call sites.

// bad
const processInput = (input) => {
  // ...
  return [left, right, top, bottom];
};

// the caller needs to think about the order of return data
const [left, __, top] = processInput(input);

// good
const processInput = (input) => {
  // ...
  return { left, right, top, bottom };
};

// the caller selects only the data they need
const { left, top } = processInput(input);

# Functions

# 10.1 ● Use function expressions instead of function declarations.

// bad
function foo() { ... }

// good
const foo = () => ...


// Generators
// bad
function* foo() { ... }

// good
const foo = function* () { ... }


// Asynchronous
// bad
async function foo () { ... }

// good
const foo = async () => { ... }

# 10.2 ● Use arrow function.

You must always use arrow functions over the function keyword if you can. It's shorter more concise lambda and does not come with an implicit state but it's lexical context.

// bad
function() { ... }

// good
() => { ... }


// Always
// bad
function () { return 'Hello World' }

// good
() => 'Hello World'


// Identity
// bad
function (x) { return x }

// good
(x) => x


// Noop
// bad
function () {}

// good
() => {}

# 10.3 ● Avoid curly braces.

If you need curly braces, your function is probably too big, has multiple concerns or is not built properly. You must avoid them.

// bad
const increment = (num) => {
  return num + 1;
};

// good
const increment = (num) => num + 1;

// bad
const make = (flower) => (color) => {
  flower(color);
  return color;
};

// good
const make = (flower) => (color) => flower(color);

# 10.4 ● Functions should have one input and one output.

You should avoid multiple function arguments.

// bad
const add = (a, b) => a + b;

// good
const add = ({ a, b }) => a + b;
const add = ([a, b]) => a + b;
const add = (a) => (b) => a + b;

# 10.5 ● Functions should have one return only.

You should avoid multiple returns per function.

// bad
const foo = (a) => {
  if (!a) {
    return bar(0);
  }
  return bar(a);
};

// good
const foo = (a) => bar(a ? a : 0);

// best
const foo = (a = 0) => bar(a);

# 10.6 ● Use default parameter syntax.

// bad
const foo = (opts) => {
  const { bar } = opts || {};
  // ...
};

// good
const foo = (opts = {}) => {
  // ...
};

# 10.7 ● Don't mutate parameters.

Manipulating objects passed in as parameters can cause unwanted variable side effects in the original caller.

// bad
const foo = (cart) => {
  cart.count = cart.count + 1;
  return cart;
};

// good
const foo = (cart) => ({ ...cart, count: cart.count + 1 });

# 10.8 ● Don't reassign parameters.

Reassigning parameters can lead to unexpected behavior, especially when accessing the arguments object. It can also cause optimization issues, especially in V8.

// bad
const foo = (bar) => {
  bar = {};
  // ...
};

// bad
const foo = (bar) => {
  if (!bar) {
    bar = {};
  }
  // ...
};

// bad
const foo = (bar) => {
  const newBar = bar || {};
  // ...
};

// good
const foo = (bar = {}) => {
  // ...
};

# 10.9 ● Don't share state.

// bad
const cart = ...
const inc = () => cart.count++;

// good
const inc = (cart = { count: 0 }) => ({ count: cart.count + 1 });

# 10.10 ● Use currying, partial application and partial evaluation only when necessary.

Partial evaluation is a useful optimization tool when used properly. Its main downside is readability, observe that the rule about curly braces is broken in the process. For that reason it should not be systematically used.

When to use

  • Partial application on functions that you intend to reuse a lot
  • Partial evaluation on functions which execution can be arbitrarily long (e.g. operations on huge arrays)

When not to use

  • When readability becomes an issue, maybe write smaller composable functions
  • Never use partial evaluation for impure functions
  • Whenever it is not absolutely necessary

# 10.11 ● Use declarative functions.

Avoid imperative functions that tend to tell the computer how to do the thing rather than declare what to do.

// bad
function fromPairs(pairs) {
  const result = {};
  let idx = 0;
  while (idx < pairs.length) {
    result[pairs[idx][0]] = pairs[idx][1];
    idx += 1;
  }
  return result;
}

// good
const fromPairs = pipe(
  map(([key, value]) => ({ [key]: value })),
  combine
);

# 10.12 ● Split code into composable functions.

// bad
const splitToKeyValuePair = (headerString) => {
  return headerString.split(",").reduce((result, current) => {
    const keyValuePair = current.split("=");
    const key = keyValuePair[0];
    const value = keyValuePair[1];
    result[key] = value;
    return result;
  }, {});
};

// good
const splitToKeyValuePair = (headerString) =>
  pipe(
    split(","),
    map(trim),
    map(split("=")),
    fromPairs,
    combine
  )(headerString);

# 10.13 ● Use point-free style for the functions composition.

You must favor point-free style.

// bad
const do = (product) => selectDiscount(selectPriceObject(product));

// good
const do = (product) => pipe(selectPriceObject, selectDiscount)(product);

# 10.14 ● Functions composition must be read from left-to-right or top-to-bottom.

For function composition with free-point style, prefer the left-to-right or top-to-bottom reading methods over the right-to-left or bottom-to-top reading methods.

import { pipe, compose } from "ramda";

const addOne = (value) => value + 1;
const multiplyByTwo = (value) => value * 2;
const toString = (value) => String(value);

// bad
const addOneMultiplyByTwoToString = (value) =>
  compose(toString, multiplyByTwo, addOne)(value);

// good
const addOneMultiplyByTwoToString = (value) =>
  pipe(addOne, multiplyByTwo, toString)(value);

Same rule apply to the tranducers

import { transduce, compose, pipe, map, filter, flip, append } from "ramda";

const numbers = [1, 2, 3, 4, 5, 6];

const isEven = (num) => num % 2 === 0;
const addTwo = (num) => num + 2;
const square = (num) => num * num;

// bad
const transducer = pipe(map(square), map(addTwo), filter(isEven));

// good
const transducer = compose(filter(isEven), map(addTwo), map(square));

transduce(transducer, flip(append), [], numbers); //=> [16, 36, 64]

# 10.15 ● Don't do dynamic code evaluation.

The eval function is the most misused feature of JavaScript. Avoid it.

Also, do not use the Function constructor. Do not pass strings to setTimeout or setInterval.

# 10.16 ● Wrap immediately invoked function expressions in parentheses.

An immediately invoked function expression is a single unit - wrapping both it, and its invocation parens, in parens, cleanly expresses this. Note that in a world with modules everywhere, you almost never need an IIFE.

// immediately-invoked function expression (IIFE)
(() => { ... })();

# 10.17 ● Never declare a function in a conditional block. Assign the function to a let variable outside the conditional block instead.

You should however avoid doing this at all costs. A likely use case would be in a universal code.

// bad
if (isServer) {
  const test = () => {};
}

// ok - avoid usage of `if`
let test;
// ...
if (isServer) {
  test = () => {};
}

// ok
let test;
// ...
isServer && (test = () => {});

# 10.18 ● Never name a parameter arguments.

This will take precedence over the arguments object that is given to every function scope.

// bad
const foo = (arguments) => {
  // ...
};

// good
const foo = (args) => {
  // ...
};

# 10.19 ● Never use arguments, opt to use rest syntax instead.

You still should follow the One input, one output rule, but when you have to use multiple arguments (for compliance purpose), you should do it this way.

The rest syntax (...) is explicit about which arguments you want pulled. Plus, rest arguments are a real Array, and not merely Array-like like arguments.

// bad
const concatenateAll = () => {
  const args = Array.prototype.slice.call(arguments);
  return args.join("");
};

// good
const concatenateAll = (...args) => args.join("");

# 10.20 ● Prefer the use of the spread operator to call variadic functions.

It's cleaner, you don't need to supply a context, and you can not easily compose new with apply.

// bad
const numbers = [1, 2, 3, 4, 5];
console.log.apply(console, numbers);

// good
const numbers = [1, 2, 3, 4, 5];
console.log(...numbers);

# Classes & Constructors

# 11.1 ● Classes and Constructors should be avoided at all cost.

You must try to avoid their usage at all cost. They are artifact from the Object-Oriented Programming paradigm.

# 11.2 ● Avoid usage of new and this keywords.

They are inherently from Object-Oriented Programming paradigm, are relied to constructors and most of the time are impures.

# Controls

# 12.1 ● Avoid if expressions.

Avoid usage of if expression, they influence imperative programming and force code blocks.

// bad
const foo = (bar?: string) => {
  if (bar) {
    return baz;
  }
  return qux;
};

// good
const foo = (bar?: string) => (bar ? baz : qux);

# 12.2 ● Use === and !== over == and !=.

Use strict === and !== operators. The == and != operators produce false positives and false negatives, so they must not be used.

// bad
(foo) => (foo != 1 ? bar : baz);
(foo) => (foo == 1 ? bar : baz);

// good
(foo) => (foo !== 1 ? bar : baz);
(foo) => (foo === 1 ? bar : baz);

# 12.3 ● Use shortcuts for booleans, but explicit comparisons for strings and numbers.

// Boolean
// bad
isValid === true && ...

// good
isValid && ...

// String
// bad
name && ...

// good
name !== "" && ...

// Number
// bad
collection.length && ...

// good
collection.length > 0 && ...

# 12.4 ● Prefer ternary expression over || and &&.

Prefer the usage of ternary expression where you are explicit of the else condition.

// ok
const foo = (bar) => (bar && baz) || 0;

// good
const foo = (bar) => (bar ? baz : { qux: true });
const foo = (bar) => (bar ? qux : 0);

# 12.5 ● Avoid unneeded ternary statements.

// bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;

// good
const foo = a || b;
const bar = !!c;
const baz = !c;

# 12.6 ● Ternaries should not be nested.

// bad
const foo = maybe1 > maybe2 ? "bar" : value1 > value2 ? "baz" : null;

// split into 2 separated ternary expressions
const maybeNull = value1 > value2 ? "baz" : null;

// good
const foo = maybe1 > maybe2 ? "bar" : maybeNull;

# 12.7 ● Use selection operators in place of control statements.

// bad
if (!isRunning) {
  startRunning();
}

// good
!isRunning && startRunning();

# 12.8 ● Use ternary expression to determine data/functions rather than execution.

// bad
const foo = (data) => (make) => (make2) => (condition) =>
  condition ? make(data) : make2(data);

// good
const foo = (make) => (make2) => (condition) => (condition ? make : make2);

foo(make)(make2)(condition)(data);

# 12.9 ● Don't use comma operator.

Having a character that is sometimes a separator and sometimes an operator is a source of confusion. This does not apply to the comma separator, which is used in object literals, array literals, and parameter lists.

// bad
const foo = ({ x, bar, baz }) => (x ? ((y = bar(x)), baz(y)) : 1);
foo({ x: 2, bar: (a) => a * 2, baz: (a: number) => a * 3 }); //=> 12

// good
const foo = ({ x, bar, baz }) => (x ? pipe(bar, baz)(x) : 1);

# 12.10 ● Do not use null, undefined nor throw an error for control flow.

Return a default value of the same type expected or use a monad to defer the missing handling decisions to the caller.

// bad
const getBody = (document) =>
  document && document.body ? document.body : undefined;

// bad
const getBody = (document) => document && document.body;

// ok - but the called probably need more than just an empty object
const getBody = (document) =>
  document && document.body ? document.body : {};

// good - we defers the decisions to the caller
const getBody = (document) =>
  Either(document).map((document) => document.body);

// bad
const getDefaultPreferredLanguage = (navigator) =>
  navigator && navigator.language ? navigator.language : undefined;

// bad
const getDefaultPreferredLanguage = (navigator) => navigator && navigator.language;

// ok - but a little over engineered
const getDefaultPreferredLanguage = (navigator) =>
  Either(navigator)
    .map((navigator) => navigator.language)
    .fork(
      () => "en-US"
      (language) => language
    );

// good
const getDefaultPreferredLanguage = (navigator) =>
  (navigator && navigator.language) || "en-US";

# 12.11 ● Asynchronous processing should be encapsulated in the Future monad.

Future((reject, resolve) =>
  fetch("https://api.awesome.com/catOfTheDay")
    .then((res) => (res.ok ? resolve(res) : Promise.reject(res)))
    .catch(reject)
).fork(
  (err) => console.log("There was an error fetching the cat of the day :("),
  (cat) => console.log(`Cat of the day: ${cat}`)
);
//=> 'Cat of the day: Garfield'

# Loops

# 13.1 ● Loop constructs such as for and while must be avoided in order to use composable methods.

Loops are inherently imperative. They also mix concerns. Iteration and execution are two different concerns that, if handled separately from each other, result in more flexible code.

That's why loop constructs such as for and while should be avoided in order to use composable methods.

Use:

  • map()
  • every()
  • filter()
  • find()
  • findIndex()
  • reduce()
  • some()
  • etc...

to iterate over arrays, and:

  • Object.keys()
  • Object.values()
  • Object.entries()

to produce arrays so you can iterate over objects.

const numbers = [1, 2, 3, 4, 5];

// bad
let sum = 0;
for (let num of numbers) {
  sum += num;
}

// bad - forEach is not composable
let sum = 0;
numbers.forEach((num) => {
  sum += num;
});

// good
const sum = numbers.reduce((total, num) => total + num, 0);

// bad
const increasedByOne = [];
for (let i = 0; i < numbers.length; i++) {
  increasedByOne.push(numbers[i] + 1);
}

// bad - forEach is not composable
const increasedByOne = [];
numbers.forEach((num) => {
  increasedByOne.push(num + 1);
});

// good
const increasedByOne = numbers.map((num) => num + 1);

# 13.2 ● Write recursive functions with a tail recursion.

// bad
const factorial = (n) => (n === 0 ? 1 : n * factorial(n - 1));

// good
const factorial = (n, acc = 1) => (n === 0 ? acc : factorial(n - 1, n * acc));

# Modules

# 14.1 ● Always use modules import-export over a non-standard module system.

You can always transpile to your preferred module system.

// bad
const someModule = require("./some-module");
exports.foo = someModule.foo;

// bad
import someModule from "./some-module";
const foo = someModule.foo;
export { foo };

// good
import { foo } from "./some-module";
export { foo };

# 14.2 ● Do not use default exports.

Importing modules must give a name to these values, which can lead to inconsistencies in naming across modules.

// bad
const foo = () => { ... }
export default foo;

// good
export const foo = () => { ... }

# 14.3 ● Do not export directly from an import.

Although the one-liner is concise, having one clear way to import and one clear way to export makes things consistent.

// bad
export { foo } from "./some-module";

// good
import { foo } from "./some-module";
export default foo;

# 14.4 ● Only import from a path in one place.

Having multiple lines that import from the same path can make code harder to maintain.

// bad
import foo from "foo";
// … some other imports … //
import { bar, baz } from "foo";

// good
import foo, { bar, baz } from "foo";

# 14.5 ● Put all import's above non-import statements.

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();

# 14.6 ● Do not export mutable bindings.

Mutation must be avoided in general, but in particular when exporting mutable bindings. While this technique may be needed for some special cases, in general, only constant references should be exported.

// bad
let foo = 3;
export { foo };

// good
const FOO = 3;
export { FOO };

# 14.7 ● Disallow Webpack loader syntax in module import statements.

Since using Webpack syntax in the imports couples the code to a module bundler. Prefer using the loader syntax in webpack.config.js.

// bad
import fooSass from "css!sass!foo.scss";
import barCss from "style!css!bar.css";

// good
import fooSass from "foo.scss";
import barCss from "bar.css";

# 14.8 ● Do not include JavaScript nor TypeScript filename extensions.

Including extensions inhibits refactoring, and inappropriately hardcodes implementation details of the module you're importing in every consumer.

// bad
import foo from "./foo.js";
import bar from "./bar.jsx";
import baz from "./baz/index.jsx";
import qux from "./qux.ts";
import quux from "./quux.tsx";
import corge from "./corge/index.tsx";

// good
import foo from "./foo";
import bar from "./bar";
import baz from "./baz";
import qux from "./qux";
import quux from "./quux";
import corge from "./corge";

# Whitespace

# 15.1 ● Leave a blank line after blocks and before the next statement.

// bad
const obj = {
  foo: () => {},
  bar: () => {},
};
return obj;

// good
const obj = {
  foo: () => {},

  bar: () => {},
};

return obj;

// best
const foo = () => {};

const bar = () => {};

const obj = {
  foo,
  bar,
};

return obj;

// bad
const arr = [() => {}, () => {}];
return arr;

// good
const arr = [() => {}, () => {}];

return arr;

# 15.2 ● Do not use multiple blank lines to pad your code.

// bad
const foo = () => {
  const bar = "bar";

  const baz = "baz";

  const qux = () => "qux";
};

// good
const foo = () => {
  const bar = "bar";
  const baz = "baz";

  const qux = () => "qux";
};

# Comments

# 16.1 ● Start all comments with a space to make it easier to read.

// bad
//is current tab
const active = true;

// good
// is current tab
const active = true;

// bad
/**
 *make() returns a new element
 *based on the passed-in tag name
 */
const make = (tag) => {
  // ...
  return element;
};

// good
/**
 * make() returns a new element
 * based on the passed-in tag name
 */
const make = (tag) => {
  // ...
  return element;
};

# 16.2 ● Use // FIXME: to annotate problems.

const calculator = (args) => {
  // FIXME: shouldn't use a global here
  total = 0;
  // ...
};

# 16.3 ● Use // TODO: to annotate solutions to problems.

const calculator = (args) => {
  // TODO: total should be configurable by an options param
  let total = 0;
  // ...
};

# 16.4 ● Use // IMPURE: to identify impurity.

To help identify them quickly, impure functions or impure function groups must be commented with // IMPURE: or put on separate specialised files.

const upper = (str) => str.toUpperCase();
const selectBody = (res) => res.body;

// IMPURE: getHttp is impure
const requestBodyToUpperCase = pipe(getHttp, selectBody, upper);