Google JavaScript Style Guide: Takeaways for TypeScript Developers

by | Apr 6, 2018

Google has published a JavaScript Style Guide.  I personally like style guides as they give direction, especially to a team of developers, on the best practice way of formatting, naming and using code.  I am an Angular developer (lately) and I follow the Angular Style Guide as best as I can.  There is even a tool to help you follow the guide called codelyzer.

So why do I care about the JavaScript Style Guide published by Google?  Good question for the following reasons:

  • Angular uses TypeScript, not JavaScript
  • Angular already has a style guide
  • TypeScript is transpiled into perfect JavaScript

The answer is simple:  because TypeScript is a super-set of JavaScript and the Angular style guide is specific to the Angular ecosystem.  The Google JavaScript Style Guide goes to the language level best practices.  There is definitely something I can learn to help me code better in this style guide.  And yes TypeScript is transpiled into perfect JavaScript but what if my next contract is doing React with JavaScript?  Best to learn best practices now before it becomes a bad habit.

I’m not going to cover everything in the JavaScript style guide, just things that TypeScript developers would care about.  For example, as a TypeScript developer, I’m not going to stop using import and export statements because they aren’t finalized yet.  Also, if you don’t want to follow something in the guide, that’s fine.  You don’t have to convince me in the comments of your point of view.  I’m just distilling what I think are important takeaways for TypeScript developers from the Google JavaScript Style Guide.  (I’m already trying to limit the backlash that can come from writing this type of post as this can become a religious war quickly…even the developers here at Intertech are filling up Slack with why semi-colons are evil!)

Let’s get to it!  I’m going to cover three sections (partially – not the whole section) of the JavaScript Style Guide:  Formatting, Language Features and Naming.

 

Formatting

Braces are used for all control structures (if, else, for, etc.) – This is basically saying that you should use curly braces { } even if there is only one line of code in the block.

if (person.type === 'Jedi') {
  person.name += 'Jedi Master';
}

// This is the only exception:
if (person == null) return;

NOT:
if (person.type === 'Jedi') person.name += 'Jedi Master';
OR:
if (person.type === 'Jedi')
  person.name += 'Jedi Master';

Nonempty blocks: K&R style – The takeaway for me on this one was this statement:  there is no line break after the brace if it is followed by else, catch, while, or a comma, semicolon, or right-parenthesis.  I think I break this one a lot coming from a C# background since it auto-formats on a separate line.

try {
  this.doSomething();
} catch(err) {
  this.handleError(err);
}

NOT:
try {
  this.doSomething();
}
catch(err) {
  this.handleError(err);
}

Block indentation: +2 spaces – Indent with two spaces every time a block is opened.  The code above illustrates the two spaces indent.  I include this one because I wasn’t sure if 2 or 4 spaces was ‘correct’.  Later in the spec it says not to use tabs also, only spaces.

Switch statements – This covers proper indentation and spacing for a switch statement.  I include it because there is guidance (sort of) on including a space between case statements.  It is optional according to the guide.  I personally like a space in between the case statements for readability.

Semicolons are required – I didn’t even realize you could omit semicolons in JavaScript and TypeScript.  Your IDE might complain but it will transpile.  I like semicolons personally but I know that others think they are the anti-Christ and completely unnecessary.

Vertical whitespace – This one gives guidance on blank lines, reproduced here because I think it’s important (some of these are personal pet peeves):

A single blank line appears:

  1. Between consecutive methods in a class or object literal
    1. Exception: A blank line between two consecutive properties definitions in an object literal (with no other code between them) is optional. Such blank lines are used as needed to create logical groupings of fields.
  2. Within method bodies, sparingly to create logical groupings of statements. Blank lines at the start or end of a function body are not allowed.
  3. Optionally before the first or after the last method in a class or object literal (neither encouraged nor discouraged).

Multiple consecutive blank lines are permitted, but never required (nor encouraged).  (Why anyone would want multiple blank lines is beyond my understanding)

Function arguments – This one gives guidance on formatting a long list of function arguments.  Note that there is an 80 character limit in JavaScript – something TypeScript developers don’t really think about.  There are three options shown but I personally prefer the last one (note the 4 space indent of the arguments):

helloWorld(
    argumentNumber1,
    argumentNumber2,
    argumentNumber3,
    argumentNumber4) {
  let x = 1;
}

Language Features

Use const and let – This is under the local variable declarations section and is saying use either const or let instead of var.  Use const unless the variable needs to be reassigned.  This is something I haven’t followed very well but will from now on…I’ve been using let instead of const by default.

One variable per declaration – Another under the local variable declarations section, the guidance is not to bunch variable declarations together on one line:

function helloWorld() {
  const x = 1;
  const y = 2;
}

NOT:
function helloWorld() {
  const x = 1, y = 2;
}

Use trailing commas – This is under the Array Literals and Object Literals section and is telling us to add a trailing comma at the end like this:

const titleArray = [
  'Jedi',
  'Sith',
];

OR:
const obj = {
  title,
  name,
};

NOT:
const titleArray = [
  'Jedi',
  'Sith'
];
  • For those wondering why this is done, Google gives no guidance but it really is for source control changes – you only want one line shown as changed instead of two (if you have to add a comma).
  • To be honest, I don’t like the looks of it coming from a C# background where it is not valid to add a trailing comma.  But in JavaScript, this is valid.

Destructuring – Still under the Array Literals section, this gives guidance on how best to do destructuring:

const [a, b, c, ...rest] = [5, 10, 15, 20, 25, 30, 35, 40];

console.log(`a = ${a}, b = ${b}, c = ${c}, rest = ${rest}`);
  • This outputs:  “a = 5, b = 10, c = 15, rest = 20,25,30,35,40”

Spread operator – Last one under the Array Literals section, this gives guidance on the spread operator in arrays for flattening other arrays (as opposed to Array.prototype functions):

const array1 = ['Hello', 'World'];
const array2 = ['JavaScript', 'Style', 'Guide'];

const spreadArray1 = [...array1];
const spreadArrayBoth = [...array1, ...array2];

console.log(`spreadArray1: ${spreadArray1} - spreadArrayBoth: ${spreadArrayBoth}`);
  • This outputs:  spreadArray1: Hello,World – spreadArrayBoth: Hello,World,JavaScript,Style,Guide
  • For this one, I didn’t realize it was best to use the spread operator to concatenate two arrays

Do not mix quoted and unquoted keys – Under the Object Literal section, this forbids mixing quoted and unquoted keys like so:

const obj = {
  hello: 'blah',
  world: 'yep',
};

const obj = {
  hello: 'blah',
  'world': 'nope',
};

Shorthand properties – Still under Object Literal section, this says you can use shorthand properties on object literals (recopying their example:)

const foo = 1;
const bar = 2;
const obj = {
  foo,
  bar,
  method() { return this.foo + this.bar; },
};
assertEquals(3, obj.method());

Getters and Setters – Under the Classes section, this advises you not to use getter/setter properties.  It does have a note about Angular in there to use it sparingly.  Another important note is that getters must not change observable state.

Overriding toString – Still under the Classes section, it is fine to override the toString method but it must always succeed, never have visible side effects and shouldn’t call other methods as you can create an infinite loop.

Arrow functions – Under the Functions section, says to prefer arrow functions over the function keyword as it helps with ‘this’ difficulties.  In TypeScript, I’ve run into this with methods that are passed to other methods.  It is fixed by making the passed-in method an arrow function as shown:

helloWorld = () => {
  console.log(this.name);
};

Use single quotes – Use single quotes for string literals instead of double quotes.  If literal contains single quote, use a template string (use back-tick quotes – upper left corner of keyboard) instead:

`Rich's world`

Template strings – Use template strings for multi-line string concatenation especially when using multiple string literals.  Indentation rules don’t apply for multi-line strings because it might impact the spacing in the string.

Exceptions – Under the Control Structures section, this gives the following guidance:

  • Always throw Error objects or subclasses, not string literals, etc.
  • Always use ‘new’ when constructing an Error
  • Use custom exceptions to convey more info
  • Rarely use empty catch blocks but if you do, add a comment as to why

 

Naming

Rules common to all identifiers – guidance on naming identifiers, these are the highlights:

  • Only ASCII letters and digits (of course Angular devs use $ for observables and it is noted as an exception)
  • Use descriptive names, not worrying about length
  • Don’t abbreviate unless it is well known like ‘num’
  • Don’t use Hungarian notation

Method names – guidance on best ways to name methods, these are the highlights:

  • Use lowerCamelCase
  • Private method names end with a trailing underscore
  • Use verb or verb phrases like doSomething or run

Enum names – guidance on naming enums and the values within them:

  • Name of enum in UpperCamelCase
  • Should be singular nouns
  • Items within the enums use CONSTANT_CASE

Constant names – guidance on naming constants.

  • Use all upper case letters
  • Separate words with underscore
  • Nouns or noun phrases

Camel case: defined – this is a good section on how to convert an English phrase into a camel case variable name.  Reprinted here for convenience but go to the site for examples, etc.

  1. Convert the phrase to plain ASCII and remove any apostrophes. For example, Müller’s algorithm might become Muellers algorithm.
  2. Divide this result into words, splitting on spaces and any remaining punctuation (typically hyphens).
    • Recommended: if any word already has a conventional camel case appearance in common usage, split this into its constituent parts (e.g., AdWords becomes ad words). Note that a word such as iOS is not really in camel case per se; it defies any convention, so this recommendation does not apply.
  3. Now lowercase everything (including acronyms), then uppercase only the first character of:
    • … each word, to yield upper camel case, or
    • … each word except the first, to yield lower camel case
  4. Finally, join all the words into a single identifier.

 

Conclusion

The preceding list of Google JavaScript Style Guide entries is good not only for JavaScript but also TypeScript languages. Developers can improve their code readability and use the proper language constructs by following some or all of the guide. Of course, take away what you like and leave the rest. As for me, I have learned some things by reading the guide and will start using them immediately.