Course Style Guide


TypeScript (JavaScript)


Introduction

This is the official style guide for my ICS4U course. You are expected to adhere to the rules of this style guide. Much of this style guide is adapted from Google's style guide for JavaScript and TypeScript but is less detailed. However, there are components of this style guide that goes against Google's style guide.

File Names

File names must be all lowercase and my include underscore _ or dashes -, but no additional punctuation.

Formatting

Braces

Braces must be used for all control structures (i.e. if, else, for, etc).

Disallowed

if (something) doSomething();

Exception: A simple if statement that can fit entirely on a single line with no wrapping (and that doesn’t have an else) may be kept on a single line with no braces when it improves readability. This is the only case in which a control structure may omit braces and newlines.

if (something) doSomething();
Nonempty blocks: K&R style
  • No line break before the opening brace.
  • Line break after the opening brace
  • Line break before the closing brace
  • Line break after the closing brace if that brace terminates a statement or the body of a function or class statement, or a class method. Specifically, there is no line break after the brace if it is followed by else, catch, while, or a comma, semicolon, or right-parenthesis.

Example:

class InnerClass { constructor() {} public method(foo: any) { if (condition(foo)) { try { // Note: this might fail. something(); } catch (err) { recover(); } } } }
Block indentation: 2 spaces

Each time a new block or block-like construct is opened, the indent increases by two spaces. When the block ends, the indent returns to the previous indent level. The indent level applies to both code and comments throughout the block.

One statement per line

Each statement is followed by a line-break

Semicolons are required

Every statement must be terminated with a semicolon.

Function arguments

Prefer to put all function arguments on the same line as the function name. If doing so would exceed the 80-column limit, the arguments must be line-wrapped in a readable way. To save space, you may wrap as close to 80 as possible, or put each argument on its own line to enhance readability. Indentation should be four spaces. Aligning to the parenthesis is allowed, but discouraged. Below are the most common patterns for argument wrapping:

// Arguments start on a new line, indented four spaces. Preferred when the // arguments don't fit on the same line with the function name (or the keyword // "function") but fit entirely on the second line. Works with very long // function names, survives renaming without reindenting, low on space. doSomething( descriptiveArgumentOne, descriptiveArgumentTwo, descriptiveArgumentThree) { // … } // If the argument list is longer, wrap at 80. Uses less vertical space, // but violates the rectangle rule and is thus not recommended. doSomething(veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo, tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) { // … } // Four-space, one argument per line. Works with long function names, // survives renaming, and emphasizes each argument. doSomething( veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo, tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) { // … }

Language Features

Identifiers
StyleCategory
UpperCamelCaseclass / interface / type / enum / decorator / type parameters
lowerCamelCasevariable / parameter / function / method / property / module alias
CONSTANT_CASEglobal constant values, including enum values
Descriptive Names

Names must be descriptive and clear to a new reader. Do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word.

Exception: Variables that are in scope for 10 lines or fewer, including arguments that are not part of an exported API, may use short (e.g. single letter) variable names.

Constants

Immutable: CONSTANT_CASE indicates that a value is intended to not be changed, and may be used for values that can technically be modified (i.e. values that are not deeply frozen) to indicate to users that they must not be modified.

const UNIT_SUFFIXES = { 'milliseconds': 'ms', 'seconds': 's', }; // Even though per the rules of JavaScript UNIT_SUFFIXES is // mutable, the uppercase shows users to not modify it.
Variables

Declare all local variables with either const or let. Use const by default, unless a variable needs to be reassigned. The var keyword must not be used.

One variable per declaration

Every local variable declaration declares only one variable: declarations such as let a = 1, b = 2; are not used.

Declare when needed, initialized as soon as possible

Local variables are not habitually declared at the start of their containing block or block-like construct. Instead, local variables are declared close to the point they are first used (within reason), to minimize their scope.

Declaring types

Variables must always be declared with their type. Functions must always be declared with their return type.

Declaring generics

Generics are declared using a single capitalized letter such as: T.

function foo<T>(arg: T): T { return arg; }
Trailing commas

Do not use trailing commas for Arrays or Object literals.

Classes

Constructors

Constructors are optional. Subclass constructors must call super() before setting any fields or otherwise accessing this. Interfaces should declare non-method properties in the constructor.

Visibility

All visibility modifers must be applied for fields, properties, and methods.

class Foo { public bar1: number; protected bar2: string; private bar3: boolean; public baz() { } }
Field initializers

If a class member is not a parameter, initialize it where it's declared, which sometimes lets you drop the constructor entirely.

BAD

class Foo { private userList: string[]; constructor() { this.userList = []; } }

GOOD

class Foo { private userList: string[] = []; }
No #private fields

Do not use private fields (also known as private identifiers):

class Clazz { #ident = 1; }

Instead, use TypeScript's visibility annotations:

class Clazz { private ident = 1; }

Comments

General

Explain code as needed, where possible.

Use comments to explain code: What does it cover, what purpose does it serve, why is respective solution used or preferred?

JSDoc

JSDoc is used on all classes, fields, and methods.

General form

The basic formatting of JSDoc blocks is as seen in this example:

/** * Multiple lines of JSDoc text are written here, * wrapped normally. * @param {number} arg A number to do something to. */ function doSomething(arg) {}

or in this single-line example:

/** @const @private {!Foo} A short bit of JSDoc. */ this.foo_ = foo;

If a single-line comment overflows into multiple lines, it must use the multi-line style with /** and */ on their own lines.

Many tools extract metadata from JSDoc comments to perform code validation and optimization. As such, these comments must be well-formed.

Omit comments that are redundant with TypeScript

For example, do not declare types in @param or @return blocks, do not write @implements, @enum, @private, @override etc. on code that uses the implements, enum, private, override etc. keywords.

Make comments that actually add information

For non-exported symbols, sometimes the name and type of the function or parameter is enough. Code will usually benefit from more documentation than just variable names though!

Avoid comments that just restate the parameter name and type, e.g.

/** @param fooBarService The Bar service for the Foo application. */

Because of this rule, @param and @return lines are only required when they add information, and may otherwise be omitted.

/** * POSTs the request to start coffee brewing. * @param amountLitres The amount to brew. Must fit the pot size! */ brew(amountLitres: number, logger: Logger) { // ... }

HTML & CSS


Indentation

Indent by 2 spaces at a time.

Don’t use tabs or mix tabs and spaces for indentation.

<ul> <li>Fantastic <li>Great </ul>
.example { color: blue; }

Capitalization

Use only lowercase.

All code has to be lowercase: This applies to HTML element names, attributes, attribute values (unless text/CDATA), CSS selectors, properties, and property values (with the exception of strings).

<!-- Not recommended --> <A HREF="/">Home</A>
<!-- Recommended --> <img src="google.png" alt="Google">
/* Not recommended */ color: #E5E5E5;
/* Recommended */ color: #e5e5e5;
General formatting

Use a new line for every block, list, or table element, and indent every such child element.

Independent of the styling of an element (as CSS allows elements to assume a different role per display property), put every block, list, or table element on a new line.

Also, indent them if they are child elements of a block, list, or table element.

(If you run into issues around whitespace between list items it’s acceptable to put all li elements in one line. A linter is encouraged to throw a warning instead of an error.)

<blockquote> <p><em>Space</em>, the final frontier.</p> </blockquote>
<ul> <li>Moe <li>Larry <li>Curly </ul>
<table> <thead> <tr> <th scope="col">Income <th scope="col">Taxes <tbody> <tr> <td>$ 5.00 <td>$ 4.50 </table>
Class naming

Use meaningful or generic class names.

Instead of presentational or cryptic names, always use class names that reflect the purpose of the element in question, or that are otherwise generic.

Names that are specific and reflect the purpose of the element should be preferred as these are most understandable and the least likely to change.

Generic names are simply a fallback for elements that have no particular or no meaning different from their siblings. They are typically needed as “helpers.”

Using functional or generic names reduces the probability of unnecessary document or template changes.

/* Not recommended: meaningless */ .yee-1901 {} /* Not recommended: presentational */ .button-green {} .clear {}
/* Recommended: specific */ .gallery {} .login {} .video {} /* Recommended: generic */ .aux {} .alt {}

Class name style

Use class names that are as short as possible but as long as necessary.

Try to convey what a class is about while being as brief as possible.

Using class names this way contributes to acceptable levels of understandability and code efficiency.

/* Not recommended */ .navigation {} .atr {}
/* Recommended */ .nav {} .author {}
Class name delimiter

Separate words in class names by a hyphen.

Do not concatenate words and abbreviations in selectors by any characters (including none at all) other than hyphens, in order to improve understanding and scannability.

/* Not recommended: does not separate the words “demo” and “image” */ .demoimage {} /* Not recommended: uses underscore instead of hyphen */ .error_status {}
/* Recommended */ .video-id {} .ads-sample {}