Scripting

Scripting Documentation

Index

  • Tooling

  • General ES6

  • Naming

  • Spacing and Syntax

  • Third party JS libraries

  • Typescript

  • Scoping and Initialization

  • Conditionals

  • Miscellaneous Code Smells

  • Resources

Tooling

Tools to help your team comply with Electron coding standards.

EditorConfig

Add the .editorconfig file to the root of your project. Download and install the EditorConfig plugin on your text editor (if it does not natively support it).

More information about EditorConfig

ESLint

To run ESLint manually, you will have to refer to the local version of ESLint in your project. You can do this one of two ways:

  1. Use the npm script provided in the package

    npm run lint file-name.js

    Note: This will run but it will output an NPM error. This is due to ESLint returning a non-zero exit code.

  2. Reference the local ESLint file directly.

    ./node_modules/.bin/eslint file-name.js

In addition to being able to lint manually, linting can also be run automatically via the use of git hooks. To use git hooks, either clone the template project (TBD) or:

  1. Copy the package.json file

  2. Run npm install to install husky, Prettier, and lint-staged

  3. Run git add after making changes

  4. Run git commit -m "Message". Prettier will automatically format the files and the linter will run, preventing commits if there are errors.

Also see the Prettier documentation for more information on formatting and linting with git hooks.

General ES6

Do use ES6 functionality when possible.

  • Why? This keeps our codebase consistent, cleaner, more concise, and more readable.

Do use an eslint or tslint file for your code.

Do use the .editorconfig file provided [link here].

Do use the ES6 array methods, such as .forEach, .map, .filter in lieu of a regular JS for loop with conditionals.

  • Why? This allows us to scan and understand intent

  • Why? It keeps our codebase more concise and more readable

  • Why? Array methods allow us to return a new array and keep the original one from being mutated.

  • Code Examples:

    Do this:

    array.filter(item => item.checked);

    Not this:

    let filteredItems = [];
    for (let i = 0; i < array.length; i++) {
        if (array[i].checked) {
            filteredItems.push(array[i]);
        }
    }

Do use implicit returns whenever working with a single statement returning an expression (especially with array methods).

  • Why? This makes it easier to read, especially for chained functions.

  • Code Examples:

    Do this:

    array.filter(item => item.checked);

    Not this:

    array.filter((item) => {
       if (item.checked) {
           return item;
       }
    });

Do import from a path in one place (I.e, all imports from @angular/core in one line versus two).

  • Why? Easier to find an import since they’re grouped.

  • ** Code Examples:**

    Do this:

    import { Http, RequestOptions, Headers } from '@angular/http';
    import { Router } from '@angular/router';

    Not this:

    import { Http } from '@angular/http';
    import { Router } from '@angular/router';
    import { RequestOptions, Headers } from '@angular/http';

Do use single quotes for strings.

Do use template strings instead of concatenation.

Do use the spread operator instead of Object.assign for shallow copies.

  • Why? This prevents us from mutating the original object.

  • Code Examples:

    Do this:

    let objectA = {};
    const objectB = { name: 'test', value: 'test' };
    objectA = {...objectA, ...objectB};

    Not this:

    let objectB = {};
    const objectA = { name: 'test', value: 'test' };
    objectA = Object.assign({}, objectA, objectB);

Do use dot notation for properties of objects.

Don’t use quotes around keys in a JSON object unless it’s a reserved keyword or contains a hyphen.

  • ESLint: quote-props

  • Code Examples:

    Do this:

    const jsonObject = {
      name: 'Name',
      value: 'Value'
      'kebab-case': 'Kebab Case'
    };

    Not this:

    const jsonObject = {
      'name': 'Name',
      'value': 'Value',
      'kebab-case': 'Kebab Case'
    };

Don’t use bracket notation to get properties of objects unless it's a reserved keyword, includes improper syntax (I.e., a hyphen), or is a variable.

  • Code Examples:

    Do this:

    const name = object.name;
    
    function getValueFromObject(key) {
        return object[key];
    }

    Not this:

    const name = object['name'];

Don’t set the value of a parameter in a function call; assign it to a variable and then pass it as a parameter. (Example: multiplyNumbers(10, x _ 2) should contain a variable called secondNumber where secondNumber = x _ 2).

  • Why? It makes it less readable.

  • Why? It makes it harder to debug.

  • Code Examples:

    Do this:

    const x = 10;
    const secondNumber = x * 2;
    const product = multiplyNumbers(10, secondNumber);

    Not this:

    const x = 10;
    const product = multiplyNumbers(10, x * 2);

Naming

Do use intuitive names for variables and functions to describe the intent (what it does) or what data it holds.

  • Code Examples:

    Do this:

    const displayErrors = form.errors.length;

    Not this:

    const exists = form.errors.length;

Do use lowerCamelCase for variables and UpperCamelCase for classes.

  • Code Examples:

    Do this:

    export class HomeComponent {
        let dataLoading = true;
        let filters = [];
        let sortOptions = {};
    };

    Not this:

    export class homeComponent {
        let DataLoading = true;
        let Filters = [];
        let sortoptions = {};
    };

Don’t name variables a single letter.

  • ESLint: id-length

  • Code Examples:

    Do this:

    const xAxisCoordinate = 45;

    Not this:

    const x = 45;

Spacing and Syntax

Do put a space between parameters.

  • Code Example:

    Do this:

    multiplyNumbers(1, 2, 3);

    Not this:

    multiplyNumbers(1,2,3);

Do put a space between a keyword and parenthesis, but don’t put a space before the parenthesis in functions.

Do put a space between parenthesis and curly braces in a function or code block.

Do use two spaces for tabs / indents.

Do place proper spacing when declaring variables.

  • Code Examples:

    Do this:

    const x = 0;

    Not this:

    const x=0;

Do terminate all lines with semicolons except code blocks (conditionals, iterators) and functions.

Do use curly braces around code blocks, including switch / case statements.

Do put comments on their own lines.

  • Code Examples:

    Do this:

    // this shows the error messages under fields
    let displayErrors = true;

    Not this:

    let displayErrors = true; // this shows the error messages under fields

Do put a space between the // and the comment content.

  • Code Examples:

    Do this:

    // comment

    Not this:

    //comment

Do indent chained methods on a new line.

Do use trailing commas.

  • ESLint: comma-dangle

  • Why? It allows for more accurate git diffs.

  • Code Examples:

    Do this:

    constructor(
        private router: Router,
        private formService: FormService,
      ) {}

    Not this:

    constructor(
        private router: Router,
        private formService: FormService
      ) {}

Don’t add multiple consecutive blank lines inside of code blocks except for one blank line to set apart nested code blocks.

  • Code Examples:

    Do this:

    function myFunction(form) {
        let displayErrors = !!form.errors.length;
    
        if (displayErrors) {
            return;
        }
    }

    Not this:

    function myFunction(form) {
        let displayErrors = !!form.errors.length;
      if (displayErrors) {
    
          return;
      }

    }

Third party JS libraries

Do examine the reasons why you want to introduce another JavaScript library.

Do research ways to write the functionality without using a third party library.

Do install third party libraries using NPM and save them in the package.json file.

Do check the approved third party libraries list before installing it.

  • TSLint: import-blacklist could do: bootstrap, jQuery (only for Angular projects)

Do add polyfills or uncomment the polyfills in the polyfills.ts file to ensure cross-browser functionality.

Do use vanilla ES6 over lodash when possible.

  • Why? Using ES6 ensures we’re all on the same page and consistent.

  • Why? Installing lodash without utilizing the more customized functions adds bloat and dependencies to the codebase.

Consider using another third party library if it contains multiple pieces of functionality you need for the application versus one method.

Don’t mix jQuery in with Angular.

  • TLint: import-blacklist could do: bootstrap, jQuery (only for Angular projects)

  • Why? jQuery is a heavy library and adds a lot of bloat to the codebase.

  • Why? Angular has much of the same functionality baked in (i.e., animations, Ajax calls).

  • Why? Many of the reasons for integrating jQuery is for one method (i.e., the .toggle() ) which could easily be replicated with vanilla JavaScript or Angular Animations.

  • Why? jQuery and Angular event handling tends to clash with each other.

Don’t use JavaScript if you can use CSS to provide the same interaction (I.e., transitions).

Typescript

Do use Typescript extensions for Angular and Sharepoint projects.

Consider creating models and interfaces to assign to variables instead of defaulting to a type of any.

  • Why? Creating models and interfaces allows us to prototype and extend classes.

  • Why? Creating models and interfaces acts as living documentation for the app so new developers know what properties belong on an object and how to use it.

Don’t use optional parameters in callbacks unless you mean it.

  • Why? It makes it difficult to tell what data is needed and what will be returned.

  • Why? You can pass in less arguments instead.

Don’t use types of String, Boolean, Number, and Object; instead use: string, boolean, number, object.

  • TSLint: no-construct

  • Why? The uppercase types refer to non-primitive objects.

Scoping and Initialization

Do use const for declaring most complex typed (I.e., objects, arrays, functions) variables that shouldn’t be reassigned.

  • Why? Preventing reassignment helps prevent bugs caused by reusing a variable.

  • Why? It keeps all declarations in one place so we know what kind of value to expect.

  • Why? Declaring variables with const adds intention to that variable and makes it more readable for the next developer.

  • Why? Declaring variables with const allows you to utilize block scoping.

Do use let for declaring primitive typed variables and variables that should be reassigned.

  • ESLint: no-var

  • Why? Declaring variables with let adds intention to that variable and makes it more readable for the next developer.

  • Why? Declaring variables with let allows you to utilize lexical scoping.

Do use arrow functions for maintaining block scoping, especially for anonymous and callback functions.

  • Why? When writing loops, we don’t have to use an IIFE / closure to maintain the current scope of the variables.

  • Why? It doesn’t change the scope that the this keyword exists in.

Do use the literal syntax (I.e., const obj = {} ) for declaring complex typed variables.

Do remove unused variables and imports.

  • Why? Leaving unused variables and imports in reduces readability.

  • Why? Leaving unused variables and imports in makes it hard to debug or determine which variable is used for a result in the program.

Don’t chain variable assignments.

  • Why? This creates global variables.

  • Code Examples:

    Do this:

    const b = c;
    const a = b;

    Not this:

    const a = b = c;

Conditionals

Do group conditionals when nesting without an else statement.

  • Code Examples:

    Do this:

    if (displayErrors && errors.length) {
        return;
    }

    Not this:

    if (displayErrors) {
        if (errors.length) {
            return;
        }
    }

Do use guard clauses.

  • Code Example

    Do this:

    function saveUser(user) {
        if (user.errors) return;
        http.post(user).then(displaySuccessMessage);
    }

    Not this:

    function saveUser(user) {
        if (user.errors) {
            return;
        } else {
            http.post(user).then(displaySuccessMessage);
        }
    }

Do use strict equality over loose equality.

  • ESLint: eqeqeq

  • Why? It checks for the value as well as the type.

  • Code Examples:

    Do this:

    if (errors.length === 0) {
      // code here
    }

    Not this:

    if (errors.length == 0) {
      // code here
    }

Do shortcut Booleans (I.e., if (var) instead of if (var === true) or if (!var) instead of if (var === null) )

Do use parenthesis to separate complex conditional statements with multiple operators.

  • Why? Improves readability.

  • Why? Ensures the conditional is executed as intended.

  • Code Examples:

    Do this:

    if ((displayErrors && errors.length) || (notSubmitted && profileIncomplete)) {
        // code here
    }

    Not this:

    if (displayErrors && errors.length || notSubmitted && profileIncomplete) {
        // code here
    }

Don’t use ternary statements for nested conditionals if you can help it (you might need to for Sharepoint).

  • Why? This is difficult to read and maintain.

  • Code Examples:

    Do this:

    if (!conditionA) {
        return valueC;
    } else if (conditionB) {
        return valueA;
    } else {
        return valueB;
    }

    Not this:

    return (!conditionA)
        ? valueC
        : (conditionB)
        ? valueA
        : valueB

Don’t use ternary statements for assigning a Boolean value

  • Code Examples:

    Do this:

    const a = true;
    const exists = const a;

    Not this:

    const a = true;
    const exists = a ? true : false;

Don’t use an else statement when an if statement returns.

  • Why? It is unnecessary lines of code since a return exits the current code block and if the statement is not true, then the code will continue executing.

  • Code Examples:

    Do this:

    if (errors.length) {
        return;
    }
    
    this.saveUser();

    Not this:

    if (errors.length) {
        return;
    } else {
        this.saveUser();
    }

Do keep conditionals as flat as possible (don’t nest multiple conditionals if possible).

  • Why? Nested conditional statements and code blocks introduce cyclomatic complexity, making it difficult to read, understand, and maintain.

  • Code Examples:

    Do this:

    const loggedIn = true;
    const role = 'admin';
    const currentPage = 'adminDashboard';
    const notAuthorized = loggedIn && role !== 'admin' && currentPage === 'adminDashboard';
    
    if (!loggedIn || notAuthorized) return;
    if (role === 'admin' && currentPage !== 'adminDashboard') {
        // let the user see the admin sidebar
    } else {
        // let the user access the page
    }

    Not this:

    const loggedIn = true;
    const role = 'admin';
    const currentPage = 'adminDashboard';
    
    if (loggedIn == true) {
        if (role == 'admin') {
            if (currentPage == 'adminDashboard') {
                // let the user access the page
            } else {
                // let the user see the admin sidebar
            }
        } else {
            if (currentPage == 'adminDashboard') {
                // kick them back
            } else {
                // let them access the page
            }
        }
    } else {
        // kick them back
    }

Miscellaneous Code Smells

Do try to avoid coercing a variable from one type to another.

Do avoid coercing a variable to another type and then back to the original type.

Do keep functions and classes small and simple – they should perform one action.

  • Why? This follows the single responsibility principle (SRP).

  • Why? It makes code readable and maintainable.

  • Why? It makes code reusable.

  • Why? It makes code testable.

Do use a debouncer and subscription on API calls on key events.

  • Why? It keeps the program from being too chatty.

  • Why? Using a subscription means we can cancel the previous subscription when we call the API again.

Do refactor similar code structures to utilize a reusable function.

  • Code Examples:

    Do this:

    emitEvent(event, value) {
        event.emit(value);
    }

    Not this:

    emitClick(value) {
        click.emit(value);
    }
    
    emitMouseover(value) {
        mouseover.emit(value);
    }

Do refactor code if you find yourself adding too many comments about what it does.

  • Why? Explaining what a piece of code does via comments indicates the code is too complex to be readable or understandable.

  • Why? Self documenting code is more valuable than lines of comments.

Don’t leave empty code blocks.

  • ESLint: no-empty

  • Why? Adds code bloat and clouds intention.

  • Code Examples:

    Do this:

    if (checked) {
        // code here
    }

    Not this:

    function testFunction {
    }
    
    if (checked) {
        // code here
    } else {
    }

Don’t duplicate / copy & paste a code block and change a few characters in order to reuse it.

  • Why? Violates DRY (Don’t Repeat Yourself) and makes it so you can’t easily maintain your codebase (with changes and bug fixes).

  • Why? We can refactor to create a more generic function and turn the changes into parameters; this makes it easier to test.

Don’t use anonymous functions (except for some callbacks in arrow functions).

  • Why? It makes debugging / tracing more difficult when using anonymous functions.

  • Code Examples:

    Do this:

    document.addEventListener("click", handleClick);
    function handleClick() {
        document.getElementById("demo").innerHTML = "Hello World";
    }

    Not this:

    document.addEventListener("click", () => {
        document.getElementById("demo").innerHTML = "Hello World";
    });

Don’t use long parameter lists in functions.

  • ESLint: max-params

  • Why? It’s difficult to understand.

  • Why? It’s easy to omit a parameter or create inconsistencies.

  • Why? Long parameter lists are an indication the function is too complex / doing more than one thing.

  • Code Examples:

    Do this:

    function setValues(object) {
        this.secondObject = {...secondObject, ...object};
    }

    Not this:

    function setValues(name, value, status, date, time, createdBy, description) {
        this.secondObject.name = name;
        this.secondObject.value = value;
        this.secondObject.status = status;
    }

Don’t pass a value directly into a function call; assign it to a variable and pass the variable.

  • Why? Assigning a variable describes intent and makes the code more readable.

  • Code Examples:

    Do this:

    const buttonClicked = true;
    this.emitClick(buttonClicked);

    Not this:

    this.emitClick(true);

Resources:

Last updated