style-guides / JavaScript

Styleguide for JavaScript and TypeScript with a focus on readability
10 stars 6 forks source link
eslint eslint-config javascript style-guide styleguide typescript

Banner

JavaScript Styleguide

This is a normative guide on how to format JavaScript. The main aim is to get the best possible readability. It is especially optimized for ECMAScript 2015, but should work equally good with other versions.

It's assumed that the code will be optimized by code minifiers and/or package tools for usage in a production client-side environment.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Table of Contents

Installation

The easiest way to adhere to this guide is to use the included eslint-config-javascript module to check your files. In order to set it up, fist install ESLint and the module:

npm install --save-dev eslint eslint-config-javascript

Next add a eslint.config.js file to your root directory with the following content:

import eslintConfJs from "eslint-config-javascript"

export default [
  ...eslintConfJs,
]

This will load the configuration of this styleguide. Finally add a lint script to the package scripts:

"scripts": {
  "lint": "eslint --max-warnings=0 --ignore-pattern=.gitignore .",
  …
}

Now you can check your files for conformity simply by running

npm run lint

Guide

Naming Conventions

Whitespace

Reasoning:

Originally this guide required the use of tabs for indentation. The problem, however, is that a tab character can have any length (normally 2 or 4 characters wide). This undermines the purpose of monospaced fonts, where every character is supposed to have the same length to ensures alignment and predictability of the text layout.

Multi-line Statements

Semicolons

Semicolons MUST NOT be used to terminate statements except the next line starts with an (, [ or `

Commas

Inline Commas

An inline comma MUST be followed by one space character

const colors = ["green", "yellow", "red"]

instead of

const colors = ["green","yellow","red"]

Trailing Commas

Trailing commas MUST be used in Arrays

const fruits = [
  apple,
  banana,
  melon,
]

instead of

const fruits = [
  apple,
  banana,
  melon
]

… and Objects

const person = {
  firstName: "John",
  lastName: "Smith",
}

instead of

const person = {
  firstName: "John",
  lastName: "Smith"
}

Reasoning:

To add an element to the end of an Array or Object, 2 lines must be modified. This adds unnecessary visual clutter to diffs. Furthermore, elements can be more easily rearranged when they all have the same structure.

Leading Commas

No leading commas MUST be used

const fruits = [
  apple,
  banana,
  peach,
  melon,
]

instead of

const fruits = [ apple
             , banana
             , peach
             , melon
             ]

and

const person = {
  firstName: "John",
  lastName: "Smith",
  age: 30,
}

instead of

const person = { firstName: "John"
             , lastName: "Smith"
             , age: 30
             }

Variables / Constants

Read Only

Read only references to a value MUST be declared with const

const answerToEverything = 42

instead of

let answerToEverything = 42

Reassignable

Reassignable variables must be declared with let

let currentPage = 138

function turnPageOver () {
  currentPage += 1
}

instead of

const currentPage = 138

function turnPageOver () {
  currentPage += 1
}

var keyword

Variables must never be declared with var

Declaration Group

Each variable MUST be declared with exactly one const or let keyword

const name = "John"
const age = 34
const instrument = "guitar"

instead of

const name = "John",
      age = 34,
      instrument = "guitar"

Global Variables

If a variable shall be exposed globally it MUST be explicitly declared as a property of the global scope (window/global object)

window.brandName = "Stark Industries"
// or
global.brandName = "Stark Industries"

instead of

brandName = "Stark Industries"

Unassigned variables

Unassigned let variables MUST be declared last in a group of declarations

const foo = 7
let bar = 4
let baz

Operators

Position

Line breaks must occur after operators:

const sentence = "This is a " +
  "short sentence."

Except for the ternary operator:

const status = isTesting
  ? "We are currently testing"
  : "Everything operational"

Placing the ? and the : at the beginning of the line makes it easier to skim the code.

Relational Operators

Shortcuts MAY be used where appropriate:

if (name) {
  …
}

instead of

if (name !== "") {
  …
}

and

if (collection.length) {
  …
}

instead of

if (collection.length > 0) {
  …
}

Blocks

Blocks MAY be used to create an encapsulated scope

{
  const age = 34
}

{
  const age = 27
}

Conditionals

Spaces Around Condition

Before and after the condition must be a single space character.

if (testValue) {
  doSomething()
}

instead of

if(testValue){
  doSomething()
}

Blocks for Multiline Statements

Blocks MAY be omitted in single line statements but must be used for multiline statements.

if (testValue) doSomething()

// or

if (testValue) {
  doSomething()
}
if (testValue) {
  doSomething()
  doThisToo()
}

Placement of else

else MUST be placed on a newline after the if-block.

if (testValue) {
  doSomething()
}
else if (otherTestValue) {
  doSomethingElse()
}
else {
  doDefault()
}

instead of

if (testValue) {
  doSomething()
} else if (otherTestValue) {
  doSomethingElse()
} else {
  doDefault()
}

Reasoning:

Comments

Single Line

Multiline

/*
This is a
multiline comment
*/

Strings

Double quotes MUST be used for strings.

Since most languages use double quotes for strings, this reduces the friction in multi-language projects.

Objects

Arrays

Functions

Anonymous

Anonymous functions MUST start with a !

!function () {
  …
}

Reasoning:

(function () {}) can lead to errors when relying on ASI (Automatic Semicolon Insertion) or when concatenating several JavaScript files

Properties

Methods

Chaining

Indentation SHOULD be used for long method chains. At the most one method should be called per line.

$("#items")
  .find(".selected")
  .highlight()

instead of

$("#items").find(".selected").highlight()

Methods SHOULD return this to enable method chaining.

Setters

Properties SHOULD be settable via setters and via a set* method to enable chaining.

person.status = single

person
  .setName("John")
  .marryTo("Linda")
  .setStatus("married")

This enables easy traversing of structures involving several classes:

database
  .load("John")
  .setAge("34")
  .sister
  .husband
  .setName("Tom")

Getters

Retrieving of properties MUST at least be implemented via a getter.

console.log(person.age)

instead of

console.log(person.age())

Type Casting

// TODO: to date, …

General

You MAY (when it's absolutely necessary) differ from any rules of this guide to increase performance. You MUST, however, explain the reasons for not sticking to a rule in a comment.

Framework specific

jQuery

Object variables MUST be prefixed with a $

const $form = $("#myForm")

instead of

const form = $("#myForm")

Lookups MUST be cached

const $form = $("#form")

$form.css({
  "background-color": "pink"
})

// …

$form.hide()

instead of

$("#form").css({
  "background-color": "pink"
})

// …

$("#form").hide()

Best Practices

Imports

Imports and requires should be sorted by type.

  1. Native
  2. External
  3. Internal

To improve readability keep an empty newline between each section and 2 empty newlines below all imports / requires.

For example ES2015 style imports:

import path from "path"
import fs from "fs"

import lodash from "lodash"

import app from "./source/app"

const port = 1234

The same applies to CommonJS requires:

const path = require("path")
const fs = require("fs")

const lodash = require("lodash")

const app = require("./source/app")

const port = 1234

Functions

Every non-primitive function should look like this:

function doSomething (options = {}) {
    const {
        hasThis = true, // Some clear description why this is here
        isThat = false,  // More clear descriptions
        importantNumber = 1234, // The clearest description in the universe
        content = "Some thoughtful text", // Crystal clear
    } = options

    // Optional type checks
    if (typeof importantNumber !== "number") {
        throw new TypeError(
            `importantNumber must be a number and not "${importantNumber}"`
        )
    }
}

This ensures:

Related