1.0.3 • Published 4 years ago

se-javascript-style-guide v1.0.3

Weekly downloads
-
License
ISC
Repository
-
Last release
4 years ago

Simple Energy Javascript Style Guide

How to use: Read through all of the sections below to understand, generally, our preferred implementation. The Implementation Standards section should be well understood committed to memory, so-to-speak. The Syntax Rules section should be read over thoroughly, but doesn't need to be memorized since the rules are enforced by our eslint configuration package.

Please keep in mind that there are many enforced syntax rules that are not outlined in this guide (we've only highlighted the most frequently violated rules here). If you are not clear about the meaning a lint error, or you are not sure how to repair the error, please refer to the eslint documentation to remediate.

I. Implementation Standards 1. Thorough Consideration 1. Naming Things 1. Consistent References 1. Immutability 1. Object Oriented & Functional Approaches 1. Testing

II. Syntax Rules 1. References 1. Objects 1. Arrays 1. Destructuring 1. Strings 1. Functions 1. Arrow Functions 1. Classes & Constructors 1. Modules 1. Iterators and Generators 1. Variables 1. Comparison Operators & Equality 1. Blocks 1. Control Statements 1. Comments 1. Commas 1. Semicolons 1. Type Casting & Coercion 1. Accessors 1. Events

I. IMPLEMENTATION STANDARDS

Thorough Consideration

  • 1.1 In general, implementation should be an expression of thorough analysis of the problem to be solved, and careful evaluation of side-effects of various strategies. Rushing into an implementation strategy often leads to an increased level of effort and limited extendability. Don't start writing until you have planned your implementation from beginning to end - including a plan for managing deployment and maintaining backward compatibility.

    Here are some questions you should be asking when considering solutions:

    • What is the simplest solution?
    • Is this solution open for extension?
    • Have I considered a variety of possible solutions?
    • Am I using the right tool for the right job?
    • What effect will this implementation have on other areas of the system?
    • Will others be able to easily understand what I am doing?

⬆ back to top

Naming Things

  • 2.1 Do not be terse when naming things. The names you assign should clearly summarize the object being named. Consider the statement const a = b + c;. While it is a valid statement, it does not provide any context as to it's purpose. The same statement, rewritten as const totalHarvest = barleyHarvest + alfalfaHarvest; is much easier reason about because it's context is clear. Avoid short, ambiguous names; be descriptive.

    // bad
    const sh = 'corn';
    
    // good
    const springHarvest = 'corn';

  • 2.2 Use camelCase when naming objects, functions, and instances.

    // bad
    const DillPickles = {};
    const delicious_dill_pickle = {};
    function EatPickle() {}
    
    // good
    const deliciousDillPickle = {};
    function eatPickle() {}

  • 2.3 Use PascalCase only when naming constructors or classes.

    // bad
    function farmHand(options) {
      this.name = options.name;
    }
    
    const farmHand = new farmHand({
      name: 'Old McDonald',
    });
    
    // good
    class FarmHand {
      constructor(options) {
        this.name = options.name;
      }
    }
    
    const farmHand = new FarmHand({
      name: 'Old McDonald',
    });

  • 2.4 A base filename should exactly match the name of its default export. Omit index.js if importing an index from a directory.

    // file 1 contents in ./Crop/index.js
    class Crop {
      // ...
    }
    export default Crop;
    
    // file 2 contents
    export default function maxYield() { return 999; }
    
    // file 3 contents
    export default function totalAllCrops() {}
    
    // in some other file
    // bad
    import Crop from './Crop/index';
    import maxYield from './MaxYield';
    import totalAllCrops from './totalAllCrops';
    
    // good
    import Crop from './Crop';
    import maxYield from './maxYield';
    import totalAllCrops from './totalAllCrops';

  • 2.5 Use camelCase when you export-default a function. Your filename should be identical to your function’s name.

    function feedTheCattle() {
      // ...
    }
    
    export default feedTheCattle;

  • 2.6 Use PascalCase when you export a constructor / class / singleton / function library / bare object.

    const TasksOnTheFarm = {
      irrigate: {
      },
    };
    
    export default TasksOnTheFarm;

  • 2.7 Acronyms and initialisms should always be all capitalized, or all lowercased.

    // bad
    import GmoFree from './GmoFree';
    
    // good
    import GMOFree from './GMOFree';
    
    // good
    import gmoFree from './gmoFree';

  • 2.8 Use uppercase names (with underscores) for application wide constants, especially configuration variables.

    // bad
    const LOCAL_VARIABLE = 'should not be uppercased';
    
    // good
    export const API_KEY = 'SOMEKEY';
    
    // good
    export const SOME_FARM = {
      FARM_REGION: 'Colorado',
    };

  • 2.9 Name imported npm modules consistently with module name. Constructors are uppercase, functions start with lowercase

    // bad
    import classNames from 'classnames';
    import ClassNames from 'class-names';
    import classnames from 'class-names';
    
    // good
    import classnames from 'classnames'; // Function default export
    import classNames from 'class-names'; // Function default export
    
    // good
    import Classnames from 'classnames'; // Constructor default export
    import ClassNames from 'class-names'; // Constructor default export

⬆ back to top

Consistent References

  • 3.1 Do not rename a parameter reference if it's value is not modified by the function. Renaming or mapping unmodified references makes it very difficult to track values through a call chain.

      // bad
      function plantCrop(crop, farmer) {
        return {
          field: crop,
          worker: farmer,
          plantedOn: new Date(),
        }
      }
    
      function waterCrop({ field, worker, datePlanted }) {
        return {
          cropType: field,
          farmHand: worker,
          planted: datePlanted,
          wateredOn: new Date(),
        }
      }
    
      function harvestCrop({ cropType, farmHand, planted, wateredOn }) {
        const threeMonthsInMilliseconds = 7776000000;
        const threeMonthsAgo = new Date().getTime() - threeMonthsInMilliseconds;
    
        if (plantedOn < threeMonthsAgo) {
          return {
            harvested: harvestedCrop,
            farmer: farmHand,
            plantedDate: planted,
            wateredDate: wateredOn,
            harvestedOn: new Date()
          }
        }
      }
    
      const plantedCrop = plantCrop('Amy', 'Walnut');
      const wateredCrop = waterCrop(plantedCrop);
      const harvestedCrop = harvestCrop(wateredCrop);
    
      // plantedCrop.field = 'Walnut'
      // wateredCrop.cropType = 'Walnut'
      // harvestedCrop.harvested = 'Walnut'
    
      // good
      function plantCrop(crop, farmer) {
        return {
          crop,
          farmer,
          plantedOn: new Date(),
        }
      }
    
      function waterCrop({ crop, farmer, plantedOn }) {
        return {
          crop,
          farmer,
          plantedOn,
          wateredOn: new Date(),
        }
      }
    
      function harvestCrop({ crop, farmer, plantedOn, wateredOn }) {
        const threeMonthsInMilliseconds = 7776000000;
        const threeMonthsAgo = new Date().getTime() - threeMonthsInMilliseconds;
    
        if (plantedOn >  threeMonthsAgo) {
          return {
            crop,
            farmer,
            plantedOn,
            wateredOn,
            harvestedOn: new Date(),
          }
        }
      }
    
      const plantedCrop = plantCrop('Amy', 'Walnut');
      const wateredCrop = waterCrop(plantedCrop);
      const harvestedCrop = harvestCrop(wateredCrop);
    
      // plantedCrop.crop = 'Walnut'
      // wateredCrop.crop = 'Walnut'
      // harvestedCrop.crop = 'Walnut'

  • 3.2 When deriving a value from a given reference, assign a name to the derivative that contains some reference to the original name. This provides useful context for understanding the source of the new reference.

      // bad
      function washFruit(fruitName) {
        const replaced = fruitName.replace(/(moldy|dirty)/, '');
        return `Enjoy your delicious ${replaced}`;
      }
    
      // good
      function washFruit(fruitName) {
        const fruitNameCleaned = fruitName.replace(/(moldy|dirty)/, '');
        return `Enjoy your delicious ${fruitNameCleaned}`;
      }
    
      // bad
      const result = veggies.filter(veggie => veggie.ripe);
    
      // good
      const veggiesFiltered = veggies.filter(veggie => veggie.ripe);

⬆ back to top

Immutability

To avoid unwanted mutations, don't work directly on a reference. Instead, create a copy of the object first or use a pure function that constructs a new reference with transformed values. The following patterns can be used to avoid unwanted mutations on objects and arrays.

  • 4.1 Shallow copy object: Use the spread operator to copy object references. This also provides a convenience for updating values on the new copy.

    const harvest = {
      type: 'Hops',
      weight: '22kg',
    }
    
    const harvestAfterDamage = {
      ...crop,
      weight: '19kg',
    }

  • 4.2 Deep copy object: Use a recursive function to clone all deeply nested object properties.

    const harvest = {
      type: 'Hops',
      stats: {
        weight: '22kg',
        harvestedBy: 'John Farmhand'
      },
    }
    
    function cloneObject(obj) {
      const clone = {};
    
      for (let i in obj) {
        if (obj[i] !== null && typeof obj[i] === 'object') {
          clone[i] = cloneObject(obj[i]);
        } else {
          clone[i] = obj[i];
        }
      }
    
      return clone;
    }
    
    const clonedHarvest = cloneObject(crop);

  • 4.3 Copy array: Use the spread operator to copy array references. This also provides a convenience for adding values to the new copy.

    const veggies = ['tomato', 'cucumber', 'potato'];
    const moreVeggies = [...veggies, 'celery'];

  • 4.4 Shallow copy object array: Use a functional array method to return a new array of copied objects.

    const veggies = [
      {
        name: 'tomato',
        color: 'red'
      },
      {
        name: 'potato',
        color: 'brown'
      }
    ];
    
    const moreVeggies = veggies.map(veggie => {
      return {
        ...veggie
      };
    });

⬆ back to top

Object Oriented & Functional Approaches

  • 5.1 Object Oriented: When a feature you are writing relies on identity, state and methods, it is best to use an object oriented approach. Particularly, if the piece of functionality will be appropriated across a number of implementations and be extended over time, it is best to encapsulate the functionality using ES6 classes so that it is decoupled from the constraints of the application framework.

  • 5.2 Functional: When a piece of functionality is required only for transforming data and has no need to maintain identity or state, a functional implementation is preferred, especially when collected into reusable modules.

  • 5.3 Always: Even when using an OOP approach, you should endeavor to apply functional paradigms to the overall design; minimizing state changes and side-effects should always be a primary concern. When mutations are required, try to relegate them to one location so that they are easier to monitor and maintain.

⬆ back to top

Testing

  • Whichever testing framework you use, you should be writing tests!
  • 100% test coverage is a good goal to strive for, even if it’s not always practical to reach it.
  • Strive to cover all branching logic; many bugs result from poorly implemented control flow.
  • Whenever you fix a bug, write a regression test. A bug fixed without a regression test is almost certainly going to break again in the future.

⬆ back to top

II. SYNTAX RULES

References

  • 1.1 Use const for all of your references; this will help ensure immutability of reference values. If you must reassign a reference, use let. Do not use var as it is function-scoped, rather than block-scoped.

    When you access a primitive type string, number, boolean, null, undefined, and symbol, you work directly on its value. When you access a complex type like object, array, and function, you work on a reference to it's value.

    No symbols: Symbols cannot be faithfully polyfilled, so they should not be used when targeting browsers/environments that don’t support them natively.

⬆ back to top

Objects

  • 2.1 Use property value shorthand, object method shorthand, and group your shorthand properties at the beginning of your object declaration.

    const farmParam = 'some_param';
    const farmName = 'Some Farm';
    
    // bad
    const farm = {
      farmHands: [],
      hasFruit: true,
      hasVeggies: false,
      farmName: farmName,
      hasLivestock: true,
      farmParam: farmParam,
    
      addFarmHand: function (name) {
        return this.farmHands.push(name);
      }
    };
    
    // good
    const farm = {
      farmName,
      farmParam,
      farmHands: [],
      hasFruit: true,
      hasVeggies: false,
      hasLivestock: true,
    
      addFarmHand(name) {
        return this.farmHands.push(name);
      },
    };

⬆ back to top

Arrays

  • 3.1 Use push instead of direct assignment to add items to an array.

    const farmersWardrobe = [];
    
    // bad
    farmersWardrobe[farmersWardrobe.length] = 'Overalls';
    
    // good
    farmersWardrobe.push('Overalls');

  • 3.2 To convert an iterable object to an array, use spreads ... instead of Array.from.

    const fruitListing = document.querySelectorAll('.fruit__list');
    
    // good
    const fruitNodes = Array.from(fruitListing);
    
    // best
    const fruitNodes = [...fruitListing];

  • 3.3 Use Array.from for converting an array-like object to an array.

    const typesOfVeggies = { 0: 'squash', 1: 'carrot', 2: 'celery' };
    
    // bad
    const veggies = Array.prototype.slice.call(typesOfVeggies);
    
    // good
    const veggies = Array.from(typesOfVeggies);

  • 3.4 Use return statements in array method callbacks. It’s ok to omit the return if the function body consists of a single statement returning an expression without side effects.

    // good
    ['squash', 'carrot', 'celery'].map((veggie) => {
      return `I love ${veggie}`;
    });
    
    // good
    ['squash', 'carrot', 'celery'].map(veggie => `I love ${veggie}`);

⬆ back to top

Destructuring

  • 4.1 Use object destructuring when accessing and using multiple properties of an object.

    // bad
    function getFruitName(fruit) {
      const genus = fruit.genus;
      const species = fruit.species;
    
      return `${genus} ${species}`;
    }
    
    // good
    function getFruitName(fruit) {
      const { genus, species } = fruit;
      return `${genus} ${species}`;
    }
    
    // best
    function getFruitName({ genus, species }) {
      return `${genus} ${species}`;
    }

  • 4.2 Use array destructuring.

    const veggies = ['squash', 'carrot', 'celery'];
    
    // bad
    const firstVeggie = veggies[0];
    const secondVeggie = veggies[1];
    
    // good
    const [firstVeggie, secondVeggie] = veggies;

  • 4.3 Use object destructuring for multiple return values, not array destructuring.

    // bad
    function tillTheSoil(input)
      return [north, south, east, west];
    }
    
    // the caller needs to think about the order of return data
    const [north, __, east] = tillTheSoil(input);
    
    // good
    function tillTheSoil(input) {
      return { north, south, east, west };
    }
    
    // the caller selects only the data they need
    const { north, east } = tillTheSoil(input);

⬆ back to top

Strings

  • 5.1 Use single quotes '' for strings.

  • 5.2 Strings that cause the line to go over 100 characters should not be written across multiple lines using string concatenation.

    // bad
    const errorMessage = 'Growing and harvesting crops requires many tasks. ' +
      'Before the crop is planted, preparing the soil with disking, tilling ' +
      '(vertical or horizontal), and fertilizing is sometimes required.';
    
    // good
    const errorMessage = 'Growing and harvesting crops requires many tasks. Before the crop is planted, preparing the soil with disking, tilling (vertical or horizontal), and fertilizing is sometimes required.';

  • 5.3 When programmatically building up strings, use template strings instead of concatenation.

    // bad
    function enjoyFruit(fruitName) {
      return 'Golly, I sure love ' + fruitName + '?';
    }
    
    // good
    function enjoyFruit(fruitName) {
      return `Golly, I sure love ${fruitName}?`;
    }

⬆ back to top

Functions

  • 6.1 Use named function expressions instead of function declarations.

    // bad
    function pickFruit() {
      // ...
    }
    
    // bad
    const pickFruit = function () {
      // ...
    };
    
    // good
    // lexical name distinguished from the variable-referenced invocation(s)
    const pickFruit = function pickFruitInTheSummer() {
      // ...
    };

  • 6.2 Never use arguments, opt to use rest syntax ... instead.

    // bad
    function makeSmoothieFromFruits() {
      const fruits = Array.prototype.slice.call(arguments);
      return fruits.join('');
    }
    
    // good
    function makeSmoothieFromFruits(...fruits) {
      return fruits.join('');
    }

  • 6.3 Use default parameter syntax rather than mutating function arguments.

    // bad
    function makeDinner(ingredients) {
      ingredients = ingredients || {};
      // ...
    }
    
    // good
    function makeDinner(ingredients = {}) {
      // ...
    }

  • 6.4 Always put default parameters last.

    // bad
    function makeDinner(ingredients = {}, participants) {
      // ...
    }
    
    // good
    function makeDinner(participants, ingredients = {}) {
      // ...
    }

  • 6.6 Never mutate parameters.

    // bad
    function fruit(type) {
      type.name = 'melon';
    }
    
    // good
    function fruit(type) {
      const fruitName = Object.prototype.hasOwnProperty.call(type, 'name') ? type.name : 'melon';
    }

  • 6.7 Never reassign parameters.

    // bad
    function fruit(name) {
      name = 'tomato';
    }
    
    // good
    function fruit(name) {
      const fruitName = name || 'tomato';
      // ...
    }

⬆ back to top

Arrow Functions

  • 7.1 When you must use an anonymous function (as when passing an inline callback), use arrow function notation.

    // bad
    ['squash', 'carrot', 'celery'].map(function (veggie) {
      return `I love ${veggie}`;
    });
    
    // good
    ['squash', 'carrot', 'celery'].map((veggie) => {
      return `I love ${veggie}`;
    });

  • 7.2 If the function body consists of a single statement returning an expression without side effects, omit the braces and use the implicit return. Otherwise, keep the braces and use a return statement.

    // bad
    ['squash', 'carrot', 'celery'].map(number => {
      return `I love ${veggie}`;
    });
    
    // good
    ['squash', 'carrot', 'celery'].map(veggie => `I love ${veggie}`);
    
    // good
    let totalVeggies = 0;
    
    ['squash', 'carrot', 'celery'].map((veggie) => {
      totalVeggies += 1;
      return `I've eaten ${totalVeggies} veggies`;
    });
    
    // good
    ['squash', 'carrot', 'celery'].map((veggie) => ({
      name: veggie,
    }));

⬆ back to top

Classes & Constructors

  • 8.1 Always use class. Avoid manipulating prototype directly.

    // bad
    function Harvest(fields = []) {
      this.queue = [...fields];
    }
    
    Harvest.prototype.yieldCrop = function () {
      const crop = this.queue[0];
      this.queue.splice(0, 1);
      return crop;
    };
    
    // good
    class Harvest {
      constructor(fields = []) {
        this.queue = [...fields];
      }
    
      yieldCrop() {
        const crop = this.queue[0];
        this.queue.splice(0, 1);
        return crop;
      }
    }

  • 8.2 Use extends for inheritance.

    // good
    class WalnutOrchard extends Orchard {
      // ...
    }

  • 8.3 Methods can return this to help with method chaining.

    // good
    class Field {
      water() {
        this.watering = true;
        return this;
      }
    
      fertilize(height) {
        this.fertilize = true;
        return this;
      }
    }
    
    const field = new Field();
    
    field.water()
      .fertilize();

⬆ back to top

Modules

  • 9.1 Always use modules (import/export) over a non-standard module system. You can always transpile to your preferred module system.

    // bad
    const HeavyEquipment = require('./HeavyEquipment');
    module.exports = HeavyEquipment.tractor;
    
    // best
    import { tractor } from './HeavyEquipment';
    export default tractor;

  • 9.2 In modules with a single export, prefer default export over named export.

    // bad
    export function waterOrchard() {}
    
    // good
    export default function waterOrchard() {}

⬆ back to top

Iterators and Generators

  • 10.1 Don’t use iterators. Prefer JavaScript’s higher-order functions instead of loops like for-in or for-of.

    Use map() / every() / filter() / find() / findIndex() / reduce() / some() / ... 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 allVeggies = 0;
    for (let number of numbers) {
      allVeggies += number;
    }
    
    // good
    let allVeggies = 0;
    numbers.forEach((number) => {
      allVeggies += number;
    });
    
    // best
    const allVeggies = numbers.reduce((total, number) => total += number, 0);

⬆ back to top

Properties

  • 11.1 Use dot notation when accessing properties.

    const oldMcDonald = {
      farm: true,
      age: 88,
    };
    
    // bad
    const hasFarm = oldMcDonald['farm'];
    
    // good
    const hasFarm = oldMcDonald.farm;

  • 11.2 Use bracket notation [] when accessing properties with a variable.

    const oldMcDonald = {
      farm: true,
      age: 88,
    };
    
    function getProp(prop) {
      return oldMcDonald[prop];
    }
    
    const hasFarm = getProp('farm');

⬆ back to top

Variables

  • 12.1 Group all your consts and then group all your lets.

    // bad
    let i;
    const fruits = getFruits();
    let bestFruit;
    const isFarmer = true;
    let totalFruits;
    
    // good
    const isFarmer = true;
    const fruits = getFruits();
    let bestFruit;
    let i;
    let totalFruits;

  • 12.2 Assign variables where you need them, but place them in a reasonable location.

    // bad
    function checkFruitName(fruitName) {
      const name = getName();
    
      if (fruitName === 'test') {
        return false;
      }
    
      if (name === 'test') {
        this.setName('');
        return false;
      }
    
      return name;
    }
    
    // good
    function checkFruitName(fruitName) {
      if (fruitName === 'test') {
        return false;
      }
    
      const name = getName();
    
      if (name === 'test') {
        this.setName('');
        return false;
      }
    
      return name;
    }

  • 12.3 Avoid using unary increments and decrements (++, --) as they are subject to automatic semicolon insertion and can cause silent errors with incrementing or decrementing values within an application.

    // bad
    let totalHarvest = 1;
    totalHarvest++;
    --totalHarvest;
    
    // good
    let totalHarvest = 1;
    totalHarvest += 1;
    totalHarvest -= 1;

⬆ back to top

Comparison Operators & Equality

  • 12.1 Conditional statements such as the if statement evaluate their expression using coercion with the ToBoolean abstract method and always follow these simple rules:

    • Objects evaluate to true
    • Undefined evaluates to false
    • Null evaluates to false
    • Booleans evaluate to the value of the boolean
    • Numbers evaluate to false if +0, -0, or NaN, otherwise true
    • Strings evaluate to false if an empty string '', otherwise true

      if ([0] && []) {
        // true
        // an array (even an empty one) is an object, objects will evaluate to true
      }

  • 12.2 Use shortcuts for booleans, but explicit comparisons for strings and numbers.

    // bad
    if (isVeggie === true) {
      // ...
    }
    
    // good
    if (isVeggie) {
      // ...
    }
    
    // bad
    if (fruitName) {
      // ...
    }
    
    // good
    if (fruitName !== '') {
      // ...
    }
    
    // bad
    if (cropsHarvested.length) {
      // ...
    }
    
    // good
    if (cropsHarvested.length > 0) {
      // ...
    }

  • 12.3 Use braces to create blocks in case and default clauses that contain lexical declarations (e.g. let, const, function, and class).

    // bad
    switch (crop) {
      case 'walnut':
        let price = 1;
        break;
      case 'squash':
        const price = 2;
        break;
      case 'carrot':
        function countCarrots() {
          // ...
        }
        break;
      default:
        class Fruit {}
    }
    
    // good
    switch (crop) {
      case 'walnut': {
        let price = 1;
        break;
      }
      case 'squash': {
        const price = 2;
        break;
      }
      case 'carrot': {
        function countCarrots() {
          // ...
        }
        break;
      }
      default: {
        class Fruit {}
      }
    }

  • 12.4 Ternaries should not be nested and generally be single line expressions.

    // good
    const typeOfHarvest = name === 'tomato'
      ? 'fruit'
      : 'veggie';
    
    // best
    const typeOfHarvest = name === 'tomato' ? 'fruit' : 'veggie';

⬆ back to top

Blocks

  • 13.1 Use braces with all multi-line blocks.

    // bad
    if (farmer)
      return false;
    
    // good
    if (farmer) {
      return false;
    }

  • 13.2 If you’re using multi-line blocks with if and else, put else on the same line as your if block’s closing brace.

    // bad
    if (farmer) {
      rideTractor();
    }
    else {
      rideBus();
    }
    
    // good
    if (test) {
      rideTractor();
    } else {
      rideBus();
    }

  • 13.3 If an if block always executes a return statement, the subsequent else block is unnecessary. A return in an else if block following an if block that contains a return can be separated into multiple if blocks.

    // bad
    function checkType(crop) {
      if (crop.isFruit) {
        return 'Fruit';
      } else {
        return 'Veggie';
      }
    }
    
    // good
    function checkType(crop) {
      if (crop.isFruit) {
        return 'Fruit';
      }
    
      return 'Veggie';
    }
    
    // bad
    function takeMeal() {
      if (lunch) {
        return 'Get yer lunch';
      } else if (dinner) {
        return 'Get yer dinner';
      }
    }
    
    // good
    function takeMeal() {
      if (lunch) {
        return 'Get yer lunch';
      }
    
      if (dinner) {
        return 'Get yer dinner';
      }
    }

⬆ back to top

Control Statements

  • 14.1 In case your control statement (if, while etc.) gets too long or exceeds the maximum line length, each (grouped) condition could be put into a new line. The logical operator should begin the line.

    // bad
    if ((totalVeggies === 999 || period === 'evening') && theTractorNeedsRepair() && theFarmerNeedsSomeHelp()) {
      retireForTheEvening();
    }
    
    // good
    if (
      (totalVeggies === 999 || period === 'evening')
      && theTractorNeedsRepair()
      && theFarmerNeedsSomeHelp()
    ) {
      retireForTheEvening();
    }

  • 14.2 Don't use selection operators in place of control statements.

    // bad
    !isEvening && waterThemFields();
    
    // good
    if (!isEvening) {
      waterThemFields();
    }

⬆ back to top

Comments

  • 15.1 Prefixing your comments with FIXME or TODO helps other developers quickly understand if you’re pointing out a problem that needs to be revisited, or if you’re suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are FIXME: -- need to figure this out or TODO: -- need to implement.

  • 15.2 Use // FIXME: to annotate problems.

    class WalnutOrchard extends Orchard {
      constructor() {
        super();
    
        // FIXME: shouldn’t use a global here
        totalYield = 0;
      }
    }

  • 15.3 Use // TODO: to annotate solutions to problems.

    class WalnutOrchard extends Orchard {
      constructor() {
        super();
    
        // TODO: total should be configurable by an options param
        this.totalYield = 0;
      }
    }

⬆ back to top

Commas

  • 16.1 Use trailing commas for multiline arrays, objects, imports, exports and functions.

    // bad
    const veggies = [
      'carrots',
      'celery',
      'tomato'
    ];
    
    // good
    const veggies = [
      'carrots',
      'celery',
      'tomato',
    ];
    
    // bad
    const farmer = {
      firstName: 'Mark',
      lastName: 'McDonald',
      birthYear: 1957
    };
    
    // good
    const farmer = {
      firstName: 'Mark',
      lastName: 'McDonald',
      birthYear: 1957,
    };

⬆ back to top

Semicolons

  • 17.1 Use semicolons to terminate all statements because ASI is not reliable.

    // bad - returns `undefined`
    function wakeBeforeDawn() {
      return
        'Early bird gets the worm'
    }
    
    // good
    function wakeBeforeDawn() {
      return 'Early bird gets the worm';
    }

⬆ back to top

Type Casting & Coercion

  • 18.1 Strings

    // bad
    const totalYield = this.cropYield.toString(); // isn’t guaranteed to return a string
    
    // bad
    const totalYield = new String(this.cropYield); // returns an object
    
    // good
    const totalYield = String(this.cropYield);

  • 18.2 Numbers: Use Number for type casting and parseInt always with a radix for parsing strings.

    const totalAcres = '4';
    
    // bad
    const farmSize = new Number(totalAcres);
    
    // good
    const farmSize = Number(totalAcres);
    
    // bad
    const farmSize = parseInt(totalAcres);
    
    // good
    const farmSize = parseInt(totalAcres, 10);

  • 18.3 Boolean

    const totalYield = 0;
    
    // bad
    const hasCrop = new Boolean(totalYield);
    
    // good
    const hasCrop = Boolean(totalYield);
    
    // best
    const hasCrop = !!totalYield;

⬆ back to top

Accessors

  • 19.1 Do not use JavaScript getters/setters as they cause unexpected side effects and are harder to test, maintain, and reason about. Instead, if you do make accessor functions, use getVal() and setVal('hello').

    // bad
    class Orchard {
      get type() {
        // ...
      }
    
      set type(value) {
        // ...
      }
    }
    
    // good
    class Orchard {
      getType() {
        // ...
      }
    
      setType(value) {
        // ...
      }
    }

⬆ back to top