Skip to content

Latest commit

 

History

History
739 lines (528 loc) · 16.9 KB

STYLE.md

File metadata and controls

739 lines (528 loc) · 16.9 KB

The Dojo Toolkit Style Guide

Code Conventions

All names and comments MUST be written in English.

Naming

The following naming conventions MUST be used:

Construct Convention
package lower-dash-case
module exporting default class UpperCamelCase
all other modules lowerCamelCase
classes, interfaces, and type aliases UpperCamelCase
enums and enum value names UpperCamelCase
constants UPPER_CASE_WITH_UNDERSCORES or lowerCamelCase
variables lowerCamelCase or _lowerCamelCase*
parameters lowerCamelCase or _lowerCamelCase*
public properties lowerCamelCase
protected/private properties _lowerCamelCase

*: Variables and parameter names generally SHOULD NOT be prefixed with underscores, but this may be warranted in rare cases where two variables in nested scopes make sense to have the same name, while avoiding shadowing the outer variable.

The following naming conventions SHOULD be used:

Variable type Convention
Deferred dfd
Promise promise
Identifier id
Numeric iterator i, j, k, l
String iterator (for-in) key, k
Event event
Destroyable handle handle
Error object error
Options arguments options
Origin, source, from source
Destination, target, to target
Coordinates x, y, z, width, height, depth
All others Do not abbreviate
  1. All names SHOULD be as clear as necessary, SHOULD NOT be contracted just for the sake of less typing, and MUST avoid unclear shortenings and contractions (e.g. MouseEventHandler, not MseEvtHdlr or hdl or h).
  2. Names SHOULD use American English (en-us) spelling.
  3. Names representing an interface MUST NOT use "I" as a prefix (e.g. Event not IEvent).
  4. Abbreviations and acronyms SHOULD NOT be uppercase when used as a name (e.g. getXml not getXML).
  5. Collections SHOULD be named using a plural form.
  6. Names representing boolean states SHOULD start with is, has, can, or should.
  7. Names representing boolean states SHOULD NOT be negative (e.g. isNotFoo is unacceptable).
  8. Names representing a count of a number of objects SHOULD start with num.
  9. Names representing methods SHOULD be verbs or verb phrases (e.g. getValue(), not value()).
  10. Factories or non-constructor methods that generate new objects SHOULD use the verb "create".
  11. Magic numbers MUST either be represented using a constant or enum, or be prefixed with a comment representing the literal value of the number (e.g. if (event.keyCode === Keys.KEY_A) or if (event.keyCode === /* "a" */ 97)).
  12. Const variables that are used as object property aliases SHOULD follow lowerCamelCase naming conventions (e.g. const { firstName = 'firstName'} = this.properties;)

Variables

  1. All variables which are not reassigned in the block SHOULD be declared with const:

    // correct
    
    const a = 1;
    
    // incorrect
    
    var a = 1;
    let a = 1;
  2. All variables which are reassigned in the block SHOULD be declared with let:

    // correct
    
    for (let i = 0; i < items.length; i++) {
    }
    
    // incorrect
    
    for (var i = 0; i < items.length; i++) {
    }
  3. All variable declarations SHOULD use one const or let declaration per variable. The exception to this rule is the initialization expression of a for statement, and object/array destructuring (e.g. for imports). This prevents variable declarations being lost inside long lists that may also include immediate assignments:

    // correct
    
    const items = getItems();
    const length = items.length;
    let i = 0;
    let item;
    
    // also right
    
    const items = getItems();
    for (let i = 0, item; (item = items[i]); i++) {
    }
    
    // incorrect
    
    const items = getItems(), length = items.length;
    let i = 0,
    	item;
  4. Variable declarations SHOULD be grouped by declaration type; const first, then let:

    // correct
    
    const items = getItems();
    const length = items.length;
    let i = 0;
    let item;
    
    // incorrect
    
    const items = getItems();
    let item;
    const length = items.length;
    let i = 0;
  5. Variables SHOULD be declared where they are first assigned:

    // correct
    
    render(): void {
    	const items = this.getItems();
    
    	if (!items.length) {
    		return;
    	}
    
    	for (let i = 0, item; (item = items[i]); i++) {
    		this.renderItem(item);
    	}
    }
    
    // incorrect
    
    render(): void {
    	const items = this.getItems();
    	let i;
    	let item;
    
    	if (!items.length) {
    		return;
    	}
    
    	for (i = 0; (item = items[i]); i++) {
    		this.renderItem(item);
    	}
    }
  6. The most appropriate data types SHOULD be used in all cases (e.g. boolean for booleans, not number).

Coding Conventions

General

  1. Strings MUST use single quotes.

  2. Equality comparisons MUST use strict comparison operators, with the exception that comparisons matching null or undefined MAY use == null.

  3. When type coersion is necessary, it SHOULD be performed using the appropriate global function (not constructor):

    // correct
    
    let myBoolean = Boolean(something);
    let myNumber = Number(something);
    let myString = String(something);
    
    // incorrect
    
    let myBoolean = !!something;
    let myNumber = +something;
    let myString = '' + something;
    
    // also incorrect (and doesn't produce primitives, so don't do it!)
    
    let myBoolean = new Boolean(something);
    let myNumber = new Number(something);
    let myString = new String(something);
  4. When passing an anonymous function as an argument, arrow functions SHOULD be used. Implicit returns from arrow functions are allowed if they increase the code readability and the return value is not ignored:

    // correct
    
    [ 1, 2, 3 ].forEach((value) => {
    	 console.log(value);
    });
    
    const arr = [ 1, 2, 3 ].map((value) => value * 2);
    
    // incorrect
    
    [ 1, 2, 3 ].forEach((value) => console.log(value));
  5. When functions are not anonymous arguments to a function, arrow functions SHOULD NOT be used:

    // correct
    
    const fn = function (): string {
    	return 'foo';
    }
    
    // incorrect
    
    const fn = () => 'foo';
  6. this typing MUST be used if accessing this and SHOULD NOT be typed as any. Use of const self = this; SHOULD NOT be used to scope arrow functions and MAY only be used when there is a need to preseve context across a normal function or IIFC.

    // correct
    
    function foo(this: SomeType) {
    	this.arr.forEach((item) => {
    		if (this.doSomething(item)) {
    			console.log(item);
    		}
    	});
    }
    
    // incorrect
    
    function foo(this: any) {
    	this.dangerous();
    }
    
    function foo() {
    	const self = this;
    	self.arr.forEach((item) => {
    		if (self.doSomething(item)) {
    			console.log(item);
    		}
    	});
    }
  7. All code SHOULD assume it will run in strict mode. ES Modules are always parsed in strict mode and TypeScript will automatically emit modules with the 'use strict'; prolog to help ensure that compatability.

  8. arguments.callee MUST NOT be used.

  9. The debugger statement MUST NOT be used.

  10. The eval function MUST NOT be used.

  11. The radix parameter of parseInt MUST be used.

  12. There MUST NOT be unreachable code after break, catch, throw, and return statements.

  13. All imports, variables, functions, and private class members MUST be used.

TypeScript

  1. Avoid casts when possible. If a type declaration can solve the problem instead, prefer that over a cast:

    // correct
    
    const something: Something = {
    	// ...
    };
    
    // incorrect
    
    const something = <Something> {
    	// ...
    };
  2. Usage of <any> casts SHOULD be documented:

    /* need to coerce to any, because of NodeJS typings */
    const req: RootRequire = <any> require;
  3. Variable declarations SHOULD NOT include an explicit type declaration if it can be easily and safely inferred on the same line.

    // correct
    
    const count = 0;
    const message = '';
    
    // incorrect
    
    const count: number = 0;
    const message: string = '';
  4. Declarations for exported functions and public class methods SHOULD include an explicit return type declaration for clarity.

    // correct
    
    export function foo(arg1: number, arg2: string): boolean {
    	// ...
    	return true;
    }
    
    // incorrect
    
    export function foo(arg1: number, arg2: string) {
    	// ...
    	return true;
    }
  5. Constructor parameters MUST NOT use public and private modifiers.

  6. Parameter flags noImplicitAny, noImplicitThis and strictNullChecks must be enabled.

Ordering

  1. Class properties SHOULD be ordered alphabetically, case-insensitively, ignoring leading underscores, in the following order:

    • private fields (properties)
    • private methods
    • static fields (properties)
    • static methods
    • constructor
    • protected fields (properties)
    • protected methods
    • public fields (properties)
    • public methods
  2. Interface properties SHOULD be ordered alphabetically, case-insensitively, ignoring leading underscores, in the following order:

    • constructor
    • function call
    • index signature
    • properties (private, protected, then public)
    • methods (private, protected, then public)
  3. Module exports SHOULD be ordered alphabetically, case-insensitively, by identifier.

  4. Module imports SHOULD be ordered alphabetically by module ID, starting with the package name. Module imports from the current package SHOULD come last. Module imports SHOULD NOT be ordered by variable name, due to potential confusion when destructuring is involved.

  5. Functions SHOULD be declared before their use. The exceptions to this rule are functions exported from a module and methods of a class:

    // correct
    
    function getItems(): Item[] {
    	// ...
    }
    
    const items = getItems();
    
    // also correct
    
    export function one(): void {
    	two();
    }
    
    export function two(): void {
    	// ...
    }
    
    // incorrect
    
    const items = getItems();
    
    function getItems(): Item[] {
    	// ...
    }

Inline Documentation

  1. Comments documenting code entities SHOULD be written in JSDoc format. Type information SHOULD NOT be included, since it should be possible to pick up from function signatures.

    Example:

    /**
     * Produces something useful and/or interesting.
     *
     * @param foo Indicates how useful something *should* be.
     * @param bar Indicates how interesting something *should* be.
     * @template T Some sort
     * @return Something useful and/or interesting.
     */
    export function doSomethingInteresting<T>(foo: string, bar: T): SomethingInteresting {
    
    }

    Note: That typescript services used to have a formatting issue when there was not a clear line break between the code block and additional parameters list. This has been resolved, though it is preferred to have a break between the comment block and the first @param.

  2. All exports and members of exported interfaces and classes SHOULD have a JSDoc code block documenting the function, class, method, type, variable, constant, or property. Internal methods MAY also include JSDoc code blocks.

  3. JSDoc blocks SHOULD use markdown syntax for providing formatting:

    /**
     * Blocks *SHOULD* use additional `markdown` syntax to make intellisense more expressive.
     */
  4. The following block tags SHOULD be used as appropriate:

    Block Tag Notes
    @param Denotes an argument for a method or function.
    @return A description of the return value.
    @template A description of a generic slot.

Comments

  1. Comments SHOULD be used to explain why something needs to be written, not what it does. If a "what" comment seems necessary, consider rewriting the code to be clearer instead. Comments summarizing entire blocks of code are permissible.

  2. Comments indicating areas to revisit SHOULD start with TODO or FIXME to make them easy to find. Ideally, such comments should only ever appear in personal/feature branches, but may be merged at maintainers' discretion.

  3. Single line comments MUST begin with a space, i.e., // comment and not //comment.

Whitespace and Formatting

Files

  1. Files MUST use hard tabs for indentation. Spaces MAY be used for alignment (under the assumption that hard tabs align to 4 spaces).

  2. Files MUST end with a single newline character.

  3. Files MUST NOT have more than one consecutive blank line.

  4. Lines SHOULD NOT exceed 120 characters in length.

  5. Lines MUST NOT contain trailing whitespace.

Semicolons

  1. Semicolons MUST be used.

  2. Semicolons SHOULD NOT be preceded by a space.

  3. Semicolons in for statements MUST be followed by a space.

Commas

  1. Commas SHOULD be followed by a space or newline, and MUST NOT be preceded by a space.

  2. Commas SHOULD NOT appear at the beginning of lines (i.e. no leading commas).

  3. All other binary/ternary operators SHOULD be surrounded by a space on both sides, unless immediately followed by a newline, in which case the operator SHOULD precede the newline (except for .).

Colons

  1. Colons in type definitions MUST be followed by a space or newline, and MUST NOT be preceded by a space.

  2. Colons in object definitions SHOULD be followed by a space, and MUST NOT be preceded by a space.

  3. Colons in case clauses SHOULD be followed by a newline, and MUST NOT be preceded by a space. The body of each case clause SHOULD be indented one level deeper than the case clause, and SHOULD conclude with the break keyword or a // fall through comment.

Braces, Brackets, and Parentheses

  1. Parentheses immediately preceding a block expression (e.g. if, for, catch) MUST be surrounded by a space on each side:

    // correct
    
    if (foo) {
    }
    
    // incorrect
    
    if(foo){
    }
  2. The opening brace of a code block MUST be written on the same line as its statement, preceded by a space:

    // correct
    
    if (foo) {
    
    }
    
    // incorrect
    
    if (foo)
    {
    
    }
    
    if(foo){
    
    }
  3. All if, for, do, while keywords MUST have opening and closing brackets.

  4. The opening and closing brackets on objects and arrays SHOULD be surrounded by whitespace on the inside of the object literal:

    // correct
    
    let object = { foo: 'foo' };
    let array = [ obj, 'foo' ];
    let arrayOfObjects = [ { foo: 'foo' } ];
    
    // incorrect
    
    let object = {foo: 'foo'};
    let array = [obj, 'foo'];
    let arrayOfObjects = [{foo: 'foo'}];
  5. Parentheses SHOULD NOT have space on the inside:

    // correct
    
    if (foo) {
    	doSomething(foo);
    }
    
    // incorrect
    
    if ( foo ) {
    	doSomething( foo );
    }
  6. Anonymous functions SHOULD have a space between the function keyword and the opening parenthesis; named functions SHOULD NOT have a space between the function name and the opening parenthesis:

    // correct
    
    function myFunction() {
    	return function () {
    
    	};
    }
    
    // incorrect
    
    function myFunction () {
    	return function() {
    
    	}
    }
  7. Blocks with a single statement SHOULD NOT be written on the same line as the opening brace:

    // correct
    
    if (foo) {
    	bar;
    }
    
    // incorrect
    
    if (foo) { bar; }
  8. Chained methods, when cannot be expressed on a single line, SHOULD line break after the first function call and before each subsequent function call in the chain, indented further than the original block:

    // correct
    
    const promise = new Promise.resolve(() => {
    		// return some value
    	})
    	.then((result) => {
    		// do something with result
    	})
    	.catch((error) => {
    		// do something with error
    	});
    
    const body = fetchResponse.text()
    	.then((text) => {
    		// do something with text
    	});
    
    // incorrect
    
    const promise = new Promise.resolve(() => {
    		// return some value
    }).then((result) => {
    	// do something with result
    }).catch((error) => {
    	// do something with error
    });
    
    const promise = new Promise
    	.resolve(() => {
    		// return some value
    	})
    	.then((result) => {
    		// do something with result
    	});
    
    const body = fetchResponse
    	.text().then((text) => {
    		// do something with text
    	});
  9. else and while keywords SHOULD be on their own line, not cuddled with the closing brace of the previous if/do block. This is consistent with the use of all other block statements and allows comments to be placed consistently before conditional statements, rather than sometimes-before, sometimes-inside:

    // correct
    
    if (foo) {
    }
    else if (bar) {
    }
    else {
    }
    
    do {
    }
    while (baz)
    
    // incorrect
    
    if (foo) {
    } else if (bar) {
    } else {
    }
    
    do {
    } while(baz)

Labels

  1. Labels MUST only be used on do, for, while and switch statements.

    // correct
    
    loop:
    for (let i = 0; i < 10; i++) {
        break loop;
    }
    
    // incorrect
    
    console:
    console.log('1, 2, 3`);
  2. Labels MUST be defined before usage.

    // correct
    
     loop:
     for (let i = 0; i < 10; i++) {
         break loop;
     }
    
    // incorrect
    
     loop:
     for (let i = 0; i < 10; i++) {
         break loop;
     }
    
     (function() {
         for (let i = 0; i < 10; i++) {
             // label out of scope
             break loop;
         }
     })();