| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"> |
| <title>Google TypeScript Style Guide</title> |
| <link rel="stylesheet" href="javaguide.css"> |
| <script src="include/styleguide.js"></script> |
| <link rel="shortcut icon" href="/styleguide/favicon.ico"> |
| <script src="include/jsguide.js"></script> |
| </head> |
| <body onload="initStyleGuide();"> |
| <div id="content"> |
| <h1>Google TypeScript Style Guide</h1> |
| |
| |
| <section> |
| |
| <p>This guide is based on the internal Google TypeScript style guide, but it has |
| been slightly adjusted to remove Google-internal sections. Google's internal |
| environment has different constraints on TypeScript than you might find outside |
| of Google. The advice here is specifically useful for people authoring code they |
| intend to import into Google, but otherwise may not apply in your external |
| environment.</p> |
| |
| <p>There is no automatic deployment process for this version as it's pushed |
| on-demand by volunteers.</p> |
| |
| </section> |
| |
| <h2 id="introduction" class="numbered">Introduction</h2> |
| |
| |
| |
| <h3 id="terminology-notes" class="numbered">Terminology notes</h3> |
| |
| <p>This Style Guide uses <a href="https://tools.ietf.org/html/rfc2119">RFC 2119</a> |
| terminology when using the phrases <em>must</em>, <em>must not</em>, <em>should</em>, <em>should not</em>, |
| and <em>may</em>. The terms <em>prefer</em> and <em>avoid</em> correspond to <em>should</em> and <em>should |
| not</em>, respectively. Imperative and declarative statements are prescriptive and |
| correspond to <em>must</em>.</p> |
| |
| <h3 id="guide-notes" class="numbered">Guide notes</h3> |
| |
| <p>All examples given are <strong>non-normative</strong> and serve only to illustrate the |
| normative language of the style guide. That is, while the examples are in Google |
| Style, they may not illustrate the <em>only</em> stylish way to represent the code. |
| Optional formatting choices made in examples must not be enforced as rules.</p> |
| |
| |
| |
| <h2 id="source-file-basics" class="numbered">Source file basics</h2> |
| |
| |
| |
| |
| |
| <p><a id="file-encoding"></a></p> |
| |
| <h3 id="file-encoding-utf-8" class="numbered">File encoding: UTF-8</h3> |
| |
| <p>Source files are encoded in <strong>UTF-8</strong>.</p> |
| |
| <p><a id="special-characters"></a></p> |
| |
| <h4 id="whitespace-characters" class="numbered">Whitespace characters</h4> |
| |
| <p>Aside from the line terminator sequence, the ASCII horizontal space character |
| (0x20) is the only whitespace character that appears anywhere in a source file. |
| This implies that all other whitespace characters in string literals are |
| escaped.</p> |
| |
| <h4 id="special-escape-sequences" class="numbered">Special escape sequences</h4> |
| |
| <p>For any character that has a special escape sequence (<code>\'</code>, <code>\"</code>, <code>\\</code>, <code>\b</code>, |
| <code>\f</code>, <code>\n</code>, <code>\r</code>, <code>\t</code>, <code>\v</code>), that sequence is used rather than the |
| corresponding numeric escape (e.g <code>\x0a</code>, <code>\u000a</code>, or <code>\u{a}</code>). Legacy octal |
| escapes are never used.</p> |
| |
| <h4 id="non-ascii-characters" class="numbered">Non-ASCII characters</h4> |
| |
| <p>For the remaining non-ASCII characters, use the actual Unicode character (e.g. |
| <code>∞</code>). For non-printable characters, the equivalent hex or Unicode escapes (e.g. |
| <code>\u221e</code>) can be used along with an explanatory comment.</p> |
| |
| <pre><code class="language-ts good">// Perfectly clear, even without a comment. |
| const units = 'μs'; |
| |
| // Use escapes for non-printable characters. |
| const output = '\ufeff' + content; // byte order mark |
| </code></pre> |
| |
| <pre><code class="language-ts bad">// Hard to read and prone to mistakes, even with the comment. |
| const units = '\u03bcs'; // Greek letter mu, 's' |
| |
| // The reader has no idea what this is. |
| const output = '\ufeff' + content; |
| </code></pre> |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| <p><a id="modules"></a> |
| <a id="source-organization"></a></p> |
| |
| <h2 id="source-file-structure" class="numbered">Source file structure</h2> |
| |
| <p>Files consist of the following, <strong>in order</strong>:</p> |
| |
| <ol> |
| <li>Copyright information, if present</li> |
| <li>JSDoc with <code>@fileoverview</code>, if present</li> |
| <li>Imports, if present</li> |
| <li>The file’s implementation</li> |
| </ol> |
| |
| <p><strong>Exactly one blank line</strong> separates each section that is present.</p> |
| |
| |
| |
| <h3 id="file-copyright" class="numbered">Copyright information</h3> |
| |
| |
| |
| <p>If license or copyright information is necessary in a file, add it in a JSDoc at |
| the top of the file. </p> |
| |
| <p><a id="file-fileoverview"></a> |
| <a id="jsdoc-top-file-level-comments"></a></p> |
| |
| <h3 id="fileoverview" class="numbered"><code>@fileoverview</code> JSDoc</h3> |
| |
| <p>A file may have a top-level <code>@fileoverview</code> JSDoc. If present, it may provide a |
| description of the file's content, its uses, or information about its |
| dependencies. Wrapped lines are not indented.</p> |
| |
| <p>Example:</p> |
| |
| <pre><code class="language-ts good">/** |
| * @fileoverview Description of file. Lorem ipsum dolor sit amet, consectetur |
| * adipiscing elit, sed do eiusmod tempor incididunt. |
| */ |
| </code></pre> |
| |
| |
| |
| <h3 id="imports" class="numbered">Imports</h3> |
| |
| <p>There are four variants of import statements in ES6 and TypeScript:</p> |
| |
| |
| |
| <section> |
| |
| <table> |
| <thead> |
| <tr> |
| <th>Import type</th> |
| <th>Example</th> |
| <th>Use for</th> |
| </tr> |
| </thead> |
| |
| <tbody> |
| <tr> |
| <td>module[<sup>module_import]</sup> |
| </td> |
| <td><code>import * as foo from |
| '...';</code></td> |
| <td>TypeScript imports |
| </td> |
| </tr> |
| <tr> |
| <td>named[<sup>destructuring_import]</sup> |
| </td> |
| <td><code>import {SomeThing} |
| from '...';</code></td> |
| <td>TypeScript imports |
| </td> |
| </tr> |
| <tr> |
| <td>default |
| |
| </td> |
| <td><code>import SomeThing |
| from '...';</code> |
| </td> |
| <td>Only for other |
| external code that |
| requires them</td> |
| </tr> |
| <tr> |
| <td>side-effect |
| |
| |
| |
| </td> |
| <td><code>import '...';</code> |
| |
| |
| |
| </td> |
| <td>Only to import |
| libraries for their |
| side-effects on load |
| (such as custom |
| elements)</td> |
| </tr> |
| </tbody> |
| </table> |
| |
| <pre><code class="language-ts good">// Good: choose between two options as appropriate (see below). |
| import * as ng from '@angular/core'; |
| import {Foo} from './foo'; |
| |
| // Only when needed: default imports. |
| import Button from 'Button'; |
| |
| // Sometimes needed to import libraries for their side effects: |
| import 'jasmine'; |
| import '@polymer/paper-button'; |
| </code></pre> |
| |
| </section> |
| |
| <h4 id="import-paths" class="numbered">Import paths</h4> |
| |
| <p>TypeScript code <em>must</em> use paths to import other TypeScript code. Paths <em>may</em> be |
| relative, i.e. starting with <code>.</code> or <code>..</code>, |
| or rooted at the base directory, e.g. |
| <code>root/path/to/file</code>.</p> |
| |
| <p>Code <em>should</em> use relative imports (<code>./foo</code>) rather than absolute imports |
| <code>path/to/foo</code> when referring to files within the same (logical) project as this |
| allows to move the project around without introducing changes in these imports.</p> |
| |
| <p>Consider limiting the number of parent steps (<code>../../../</code>) as those can make |
| module and path structures hard to understand.</p> |
| |
| <pre><code class="language-ts good">import {Symbol1} from 'path/from/root'; |
| import {Symbol2} from '../parent/file'; |
| import {Symbol3} from './sibling'; |
| </code></pre> |
| |
| |
| |
| <p><a id="module-versus-destructuring-import"></a></p> |
| |
| <h4 id="namespace-versus-named-imports" class="numbered">Namespace versus named imports</h4> |
| |
| <p>Both namespace and named imports can be used.</p> |
| |
| <p>Prefer named imports for symbols used frequently in a file or for symbols that |
| have clear names, for example Jasmine's <code>describe</code> and <code>it</code>. Named imports can |
| be aliased to clearer names as needed with <code>as</code>.</p> |
| |
| <p>Prefer namespace imports when using many different symbols from large APIs. A |
| namespace import, despite using the <code>*</code> character, is not comparable to a |
| <q>wildcard</q> import as seen in other languages. Instead, namespace imports give a |
| name to all the exports of a module, and each exported symbol from the module |
| becomes a property on the module name. Namespace imports can aid readability for |
| exported symbols that have common names like <code>Model</code> or <code>Controller</code> without the |
| need to declare aliases.</p> |
| |
| <pre><code class="language-ts bad">// Bad: overlong import statement of needlessly namespaced names. |
| import {Item as TableviewItem, Header as TableviewHeader, Row as TableviewRow, |
| Model as TableviewModel, Renderer as TableviewRenderer} from './tableview'; |
| |
| let item: TableviewItem|undefined; |
| </code></pre> |
| |
| <pre><code class="language-ts good">// Better: use the module for namespacing. |
| import * as tableview from './tableview'; |
| |
| let item: tableview.Item|undefined; |
| </code></pre> |
| |
| <pre><code class="language-ts bad">import * as testing from './testing'; |
| |
| // Bad: The module name does not improve readability. |
| testing.describe('foo', () => { |
| testing.it('bar', () => { |
| testing.expect(null).toBeNull(); |
| testing.expect(undefined).toBeUndefined(); |
| }); |
| }); |
| </code></pre> |
| |
| <pre><code class="language-ts good">// Better: give local names for these common functions. |
| import {describe, it, expect} from './testing'; |
| |
| describe('foo', () => { |
| it('bar', () => { |
| expect(null).toBeNull(); |
| expect(undefined).toBeUndefined(); |
| }); |
| }); |
| </code></pre> |
| |
| <h5 id="jspb-import-by-path" class="numbered">Special case: Apps JSPB protos</h5> |
| |
| <p>Apps JSPB protos must use named imports, even when it leads to long import |
| lines.</p> |
| |
| <p>This rule exists to aid in build performance and dead code elimination since |
| often <code>.proto</code> files contain many <code>message</code>s that are not all needed together. |
| By leveraging destructured imports the build system can create finer grained |
| dependencies on Apps JSPB messages while preserving the ergonomics of path based |
| imports.</p> |
| |
| <pre><code class="language-ts good">// Good: import the exact set of symbols you need from the proto file. |
| import {Foo, Bar} from './foo.proto'; |
| |
| function copyFooBar(foo: Foo, bar: Bar) {...} |
| </code></pre> |
| |
| |
| |
| <h4 id="renaming-imports" class="numbered">Renaming imports</h4> |
| |
| <p>Code <em>should</em> fix name collisions by using a namespace import or renaming the |
| exports themselves. Code <em>may</em> rename imports (<code>import {SomeThing as |
| SomeOtherThing}</code>) if needed.</p> |
| |
| <p>Three examples where renaming can be helpful:</p> |
| |
| <ol> |
| <li>If it's necessary to avoid collisions with other imported symbols.</li> |
| <li>If the imported symbol name is generated.</li> |
| <li>If importing symbols whose names are unclear by themselves, renaming can |
| improve code clarity. For example, when using RxJS the <code>from</code> function might |
| be more readable when renamed to <code>observableFrom</code>.</li> |
| </ol> |
| |
| |
| |
| |
| |
| |
| |
| <h3 id="exports" class="numbered">Exports</h3> |
| |
| <p>Use named exports in all code:</p> |
| |
| <pre><code class="language-ts good">// Use named exports: |
| export class Foo { ... } |
| </code></pre> |
| |
| <p>Do not use default exports. This ensures that all imports follow a uniform |
| pattern.</p> |
| |
| <pre><code class="language-ts bad">// Do not use default exports: |
| export default class Foo { ... } // BAD! |
| </code></pre> |
| |
| <section class="zippy"> |
| |
| <p>Why?</p> |
| |
| <p>Default exports provide no canonical name, which makes central maintenance |
| difficult with relatively little benefit to code owners, including potentially |
| decreased readability:</p> |
| |
| <pre><code class="language-ts bad">import Foo from './bar'; // Legal. |
| import Bar from './bar'; // Also legal. |
| </code></pre> |
| |
| <p>Named exports have the benefit of erroring when import statements try to import |
| something that hasn't been declared. In <code>foo.ts</code>:</p> |
| |
| <pre><code class="language-ts bad">const foo = 'blah'; |
| export default foo; |
| </code></pre> |
| |
| <p>And in <code>bar.ts</code>:</p> |
| |
| <pre><code class="language-ts bad">import {fizz} from './foo'; |
| </code></pre> |
| |
| <p>Results in <code>error TS2614: Module '"./foo"' has no exported member 'fizz'.</code> While |
| <code>bar.ts</code>:</p> |
| |
| <pre><code class="language-ts bad">import fizz from './foo'; |
| </code></pre> |
| |
| <p>Results in <code>fizz === foo</code>, which is probably unexpected and difficult to debug.</p> |
| |
| <p>Additionally, default exports encourage people to put everything into one big |
| object to namespace it all together:</p> |
| |
| <pre><code class="language-ts bad">export default class Foo { |
| static SOME_CONSTANT = ... |
| static someHelpfulFunction() { ... } |
| ... |
| } |
| </code></pre> |
| |
| <p>With the above pattern, we have file scope, which can be used as a namespace. We |
| also have a perhaps needless second scope (the class <code>Foo</code>) that can be |
| ambiguously used as both a type and a value in other files.</p> |
| |
| <p>Instead, prefer use of file scope for namespacing, as well as named exports:</p> |
| |
| <pre><code class="language-ts good">export const SOME_CONSTANT = ... |
| export function someHelpfulFunction() |
| export class Foo { |
| // only class stuff here |
| } |
| </code></pre> |
| |
| </section> |
| |
| <h4 id="export-visibility" class="numbered">Export visibility</h4> |
| |
| <p>TypeScript does not support restricting the visibility for exported symbols. |
| Only export symbols that are used outside of the module. Generally minimize the |
| exported API surface of modules.</p> |
| |
| |
| |
| <h4 id="mutable-exports" class="numbered">Mutable exports</h4> |
| |
| |
| |
| <p>Regardless of technical support, mutable exports can create hard to understand |
| and debug code, in particular with re-exports across multiple modules. One way |
| to paraphrase this style point is that <code>export let</code> is not allowed.</p> |
| |
| <section> |
| |
| <pre><code class="language-ts bad">export let foo = 3; |
| // In pure ES6, foo is mutable and importers will observe the value change after a second. |
| // In TS, if foo is re-exported by a second file, importers will not see the value change. |
| window.setTimeout(() => { |
| foo = 4; |
| }, 1000 /* ms */); |
| </code></pre> |
| |
| </section> |
| |
| <p>If one needs to support externally accessible and mutable bindings, they |
| <em>should</em> instead use explicit getter functions.</p> |
| |
| <pre><code class="language-ts good">let foo = 3; |
| window.setTimeout(() => { |
| foo = 4; |
| }, 1000 /* ms */); |
| // Use an explicit getter to access the mutable export. |
| export function getFoo() { return foo; }; |
| </code></pre> |
| |
| <p>For the common pattern of conditionally exporting either of two values, first do |
| the conditional check, then the export. Make sure that all exports are final |
| after the module's body has executed.</p> |
| |
| <pre><code class="language-ts good">function pickApi() { |
| if (useOtherApi()) return OtherApi; |
| return RegularApi; |
| } |
| export const SomeApi = pickApi(); |
| </code></pre> |
| |
| <p><a id="static-containers"></a></p> |
| |
| <h4 id="container-classes" class="numbered">Container classes</h4> |
| |
| <p>Do not create container classes with static methods or properties for the sake |
| of namespacing.</p> |
| |
| <pre><code class="language-ts bad">export class Container { |
| static FOO = 1; |
| static bar() { return 1; } |
| } |
| </code></pre> |
| |
| <p>Instead, export individual constants and functions:</p> |
| |
| <pre><code class="language-ts good">export const FOO = 1; |
| export function bar() { return 1; } |
| </code></pre> |
| |
| |
| |
| <h3 id="import-export-type" class="numbered">Import and export type</h3> |
| |
| <h4 id="import-type" class="numbered">Import type</h4> |
| |
| <p>You may use <code>import type {...}</code> when you use the imported symbol only as a type. |
| Use regular imports for values:</p> |
| |
| <pre><code class="language-ts good">import type {Foo} from './foo'; |
| import {Bar} from './foo'; |
| |
| import {type Foo, Bar} from './foo'; |
| </code></pre> |
| |
| <section class="zippy"> |
| |
| <p>Why?</p> |
| |
| <p>The TypeScript compiler automatically handles the distinction and does not |
| insert runtime loads for type references. So why annotate type imports?</p> |
| |
| <p>The TypeScript compiler can run in 2 modes:</p> |
| |
| <ul> |
| <li>In development mode, we typically want quick iteration loops. The compiler |
| transpiles to JavaScript without full type information. This is much faster, |
| but requires <code>import type</code> in certain cases.</li> |
| <li>In production mode, we want correctness. The compiler type checks everything |
| and ensures <code>import type</code> is used correctly.</li> |
| </ul> |
| |
| <p>Note: If you need to force a runtime load for side effects, use <code>import '...';</code>. |
| See </p> |
| |
| </section> |
| |
| <h4 id="export-type" class="numbered">Export type</h4> |
| |
| <p>Use <code>export type</code> when re-exporting a type, e.g.:</p> |
| |
| <pre><code class="language-ts good">export type {AnInterface} from './foo'; |
| </code></pre> |
| |
| <section class="zippy"> |
| |
| <p>Why?</p> |
| |
| <p><code>export type</code> is useful to allow type re-exports in file-by-file transpilation. |
| See |
| <a href="https://www.typescriptlang.org/tsconfig#exports-of-non-value-identifiers"><code>isolatedModules</code> docs</a>.</p> |
| |
| <p><code>export type</code> might also seem useful to avoid ever exporting a value symbol for |
| an API. However it does not give guarantees, either: downstream code might still |
| import an API through a different path. A better way to split & guarantee type |
| vs value usages of an API is to actually split the symbols into e.g. |
| <code>UserService</code> and <code>AjaxUserService</code>. This is less error prone and also better |
| communicates intent.</p> |
| |
| </section> |
| |
| <p><a id="namespaces-vs-modules"></a></p> |
| |
| <h4 id="use-modules-not-namespaces" class="numbered">Use modules not namespaces</h4> |
| |
| <p>TypeScript supports two methods to organize code: <em>namespaces</em> and <em>modules</em>, |
| but namespaces are disallowed. That |
| is, your code <em>must</em> refer to code in other files using imports and exports of |
| the form <code>import {foo} from 'bar';</code></p> |
| |
| <p>Your code <em>must not</em> use the <code>namespace Foo { ... }</code> construct. <code>namespace</code>s |
| <em>may</em> only be used when required to interface with external, third party code. |
| To semantically namespace your code, use separate files.</p> |
| |
| |
| |
| <p>Code <em>must not</em> use <code>require</code> (as in <code>import x = require('...');</code>) for imports. |
| Use ES6 module syntax.</p> |
| |
| <pre><code class="language-ts bad">// Bad: do not use namespaces: |
| namespace Rocket { |
| function launch() { ... } |
| } |
| |
| // Bad: do not use <reference> |
| /// <reference path="..."/> |
| |
| // Bad: do not use require() |
| import x = require('mydep'); |
| </code></pre> |
| |
| |
| |
| <blockquote> |
| <p>NB: TypeScript <code>namespace</code>s used to be called internal modules and used to use |
| the <code>module</code> keyword in the form <code>module Foo { ... }</code>. Don't use that either. |
| Always use ES6 imports.</p> |
| </blockquote> |
| |
| <p><a id="language-rules"></a></p> |
| |
| <h2 id="language-features" class="numbered">Language features</h2> |
| |
| <p>This section delineates which features may or may not be used, and any |
| additional constraints on their use.</p> |
| |
| <p>Language features which are not discussed in this style guide <em>may</em> be used with |
| no recommendations of their usage.</p> |
| |
| <p><a id="features-local-variable-declarations"></a></p> |
| |
| <h3 id="local-variable-declarations" class="numbered">Local variable declarations</h3> |
| |
| <p><a id="variables"></a> |
| <a id="features-use-const-and-let"></a></p> |
| |
| <h4 id="use-const-and-let" class="numbered">Use const and let</h4> |
| |
| <p>Always use <code>const</code> or <code>let</code> to declare variables. Use <code>const</code> by default, unless |
| a variable needs to be reassigned. Never use <code>var</code>.</p> |
| |
| <pre><code class="language-ts good">const foo = otherValue; // Use if "foo" never changes. |
| let bar = someValue; // Use if "bar" is ever assigned into later on. |
| </code></pre> |
| |
| <p><code>const</code> and <code>let</code> are block scoped, like variables in most other languages. |
| <code>var</code> in JavaScript is function scoped, which can cause difficult to understand |
| bugs. Don't use it.</p> |
| |
| <pre><code class="language-ts bad">var foo = someValue; // Don't use - var scoping is complex and causes bugs. |
| </code></pre> |
| |
| <p>Variables <em>must not</em> be used before their declaration.</p> |
| |
| <p><a id="features-one-variable-per-declaration"></a></p> |
| |
| <h4 id="one-variable-per-declaration" class="numbered">One variable per declaration</h4> |
| |
| <p>Every local variable declaration declares only one variable: declarations such |
| as <code class="badcode">let a = 1, b = 2;</code> are not used.</p> |
| |
| <p><a id="features-array-literals"></a></p> |
| |
| <h3 id="array-literals" class="numbered">Array literals</h3> |
| |
| <p><a id="features-arrays-ctor"></a></p> |
| |
| <h4 id="array-constructor" class="numbered">Do not use the <code>Array</code> constructor</h4> |
| |
| <p><em>Do not</em> use the <code>Array()</code> constructor, with or without <code>new</code>. It has confusing |
| and contradictory usage:</p> |
| |
| |
| |
| <pre><code class="language-ts bad">const a = new Array(2); // [undefined, undefined] |
| const b = new Array(2, 3); // [2, 3]; |
| </code></pre> |
| |
| |
| |
| <p>Instead, always use bracket notation to initialize arrays, or <code>from</code> to |
| initialize an <code>Array</code> with a certain size:</p> |
| |
| <pre><code class="language-ts good">const a = [2]; |
| const b = [2, 3]; |
| |
| // Equivalent to Array(2): |
| const c = []; |
| c.length = 2; |
| |
| // [0, 0, 0, 0, 0] |
| Array.from<number>({length: 5}).fill(0); |
| </code></pre> |
| |
| |
| |
| <p><a id="features-arrays-non-numeric-properties"></a></p> |
| |
| <h4 id="do-not-define-properties-on-arrays" class="numbered">Do not define properties on arrays</h4> |
| |
| <p>Do not define or use non-numeric properties on an array (other than <code>length</code>). |
| Use a <code>Map</code> (or <code>Object</code>) instead.</p> |
| |
| <p><a id="features-arrays-spread-operator"></a></p> |
| |
| <h4 id="array-spread-syntax" class="numbered">Using spread syntax</h4> |
| |
| <p>Using spread syntax <code>[...foo];</code> is a convenient shorthand for shallow-copying or |
| concatenating iterables.</p> |
| |
| <pre><code class="language-ts good">const foo = [ |
| 1, |
| ]; |
| |
| const foo2 = [ |
| ...foo, |
| 6, |
| 7, |
| ]; |
| |
| const foo3 = [ |
| 5, |
| ...foo, |
| ]; |
| |
| foo2[1] === 6; |
| foo3[1] === 1; |
| </code></pre> |
| |
| <p>When using spread syntax, the value being spread <em>must</em> match what is being |
| created. When creating an array, only spread iterables. Primitives (including |
| <code>null</code> and <code>undefined</code>) <em>must not</em> be spread.</p> |
| |
| <pre><code class="language-ts bad">const foo = [7]; |
| const bar = [5, ...(shouldUseFoo && foo)]; // might be undefined |
| |
| // Creates {0: 'a', 1: 'b', 2: 'c'} but has no length |
| const fooStrings = ['a', 'b', 'c']; |
| const ids = {...fooStrings}; |
| </code></pre> |
| |
| <pre><code class="language-ts good">const foo = shouldUseFoo ? [7] : []; |
| const bar = [5, ...foo]; |
| const fooStrings = ['a', 'b', 'c']; |
| const ids = [...fooStrings, 'd', 'e']; |
| </code></pre> |
| |
| <p><a id="features-arrays-destructuring"></a></p> |
| |
| <h4 id="array-destructuring" class="numbered">Array destructuring</h4> |
| |
| <p>Array literals may be used on the left-hand side of an assignment to perform |
| destructuring (such as when unpacking multiple values from a single array or |
| iterable). A final <q>rest</q> element may be included (with no space between the |
| <code>...</code> and the variable name). Elements should be omitted if they are unused.</p> |
| |
| <pre><code class="language-ts good">const [a, b, c, ...rest] = generateResults(); |
| let [, b,, d] = someArray; |
| </code></pre> |
| |
| <p>Destructuring may also be used for function parameters. Always specify <code>[]</code> as |
| the default value if a destructured array parameter is optional, and provide |
| default values on the left hand side:</p> |
| |
| <pre><code class="language-ts good">function destructured([a = 4, b = 2] = []) { … } |
| </code></pre> |
| |
| <p>Disallowed:</p> |
| |
| <pre><code class="language-ts bad">function badDestructuring([a, b] = [4, 2]) { … } |
| </code></pre> |
| |
| <p>Tip: For (un)packing multiple values into a function’s parameter or return, |
| prefer object destructuring to array destructuring when possible, as it allows |
| naming the individual elements and specifying a different type for each.</p> |
| |
| |
| |
| <p><a id="features-object-literals"></a></p> |
| |
| <h3 id="object-literals" class="numbered">Object literals</h3> |
| |
| <p><a id="features-objects-ctor"></a></p> |
| |
| <h4 id="object-constructor" class="numbered">Do not use the <code>Object</code> constructor</h4> |
| |
| <p>The <code>Object</code> constructor is disallowed. Use an object literal (<code>{}</code> or <code>{a: 0, |
| b: 1, c: 2}</code>) instead.</p> |
| |
| <h4 id="iterating-objects" class="numbered">Iterating objects</h4> |
| |
| <p>Iterating objects with <code>for (... in ...)</code> is error prone. It will include |
| enumerable properties from the prototype chain.</p> |
| |
| <p>Do not use unfiltered <code>for (... in ...)</code> statements:</p> |
| |
| <pre><code class="language-ts bad">for (const x in someObj) { |
| // x could come from some parent prototype! |
| } |
| </code></pre> |
| |
| <p>Either filter values explicitly with an <code>if</code> statement, or use <code>for (... of |
| Object.keys(...))</code>.</p> |
| |
| <pre><code class="language-ts good">for (const x in someObj) { |
| if (!someObj.hasOwnProperty(x)) continue; |
| // now x was definitely defined on someObj |
| } |
| for (const x of Object.keys(someObj)) { // note: for _of_! |
| // now x was definitely defined on someObj |
| } |
| for (const [key, value] of Object.entries(someObj)) { // note: for _of_! |
| // now key was definitely defined on someObj |
| } |
| </code></pre> |
| |
| <p><a id="using-the-spread-operator"></a></p> |
| |
| <h4 id="object-spread-syntax" class="numbered">Using spread syntax</h4> |
| |
| <p>Using spread syntax <code>{...bar}</code> is a convenient shorthand for creating a shallow |
| copy of an object. When using spread syntax in object initialization, later |
| values replace earlier values at the same key.</p> |
| |
| <pre><code class="language-ts good">const foo = { |
| num: 1, |
| }; |
| |
| const foo2 = { |
| ...foo, |
| num: 5, |
| }; |
| |
| const foo3 = { |
| num: 5, |
| ...foo, |
| } |
| |
| foo2.num === 5; |
| foo3.num === 1; |
| |
| </code></pre> |
| |
| <p>When using spread syntax, the value being spread <em>must</em> match what is being |
| created. That is, when creating an object, only objects may be spread; arrays |
| and primitives (including <code>null</code> and <code>undefined</code>) <em>must not</em> be spread. Avoid |
| spreading objects that have prototypes other than the Object prototype (e.g. |
| class definitions, class instances, functions) as the behavior is unintuitive |
| (only enumerable non-prototype properties are shallow-copied).</p> |
| |
| <pre><code class="language-ts bad">const foo = {num: 7}; |
| const bar = {num: 5, ...(shouldUseFoo && foo)}; // might be undefined |
| |
| // Creates {0: 'a', 1: 'b', 2: 'c'} but has no length |
| const fooStrings = ['a', 'b', 'c']; |
| const ids = {...fooStrings}; |
| </code></pre> |
| |
| <pre><code class="language-ts good">const foo = shouldUseFoo ? {num: 7} : {}; |
| const bar = {num: 5, ...foo}; |
| </code></pre> |
| |
| <p><a id="features-objects-computed-property-names"></a></p> |
| |
| <h4 id="computed-property-names" class="numbered">Computed property names</h4> |
| |
| <p>Computed property names (e.g. <code>{['key' + foo()]: 42}</code>) are allowed, and are |
| considered dict-style (quoted) keys (i.e., must not be mixed with non-quoted |
| keys) unless the computed property is a |
| <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol">symbol</a> |
| (e.g. <code>[Symbol.iterator]</code>).</p> |
| |
| |
| |
| <p><a id="features-objects-destructuring"></a></p> |
| |
| <h4 id="object-destructuring" class="numbered">Object destructuring</h4> |
| |
| <p>Object destructuring patterns may be used on the left-hand side of an assignment |
| to perform destructuring and unpack multiple values from a single object.</p> |
| |
| <p>Destructured objects may also be used as function parameters, but should be kept |
| as simple as possible: a single level of unquoted shorthand properties. Deeper |
| levels of nesting and computed properties may not be used in parameter |
| destructuring. Specify any default values in the left-hand-side of the |
| destructured parameter (<code>{str = 'some default'} = {}</code>, rather than |
| <code class="badcode">{str} = {str: 'some default'}</code>), and if a |
| destructured object is itself optional, it must default to <code>{}</code>.</p> |
| |
| <p>Example:</p> |
| |
| <pre><code class="language-ts good">interface Options { |
| /** The number of times to do something. */ |
| num?: number; |
| |
| /** A string to do stuff to. */ |
| str?: string; |
| } |
| |
| function destructured({num, str = 'default'}: Options = {}) {} |
| </code></pre> |
| |
| <p>Disallowed:</p> |
| |
| <pre><code class="language-ts bad">function nestedTooDeeply({x: {num, str}}: {x: Options}) {} |
| function nontrivialDefault({num, str}: Options = {num: 42, str: 'default'}) {} |
| </code></pre> |
| |
| |
| |
| |
| |
| <p><a id="formatting-class-literals"></a> |
| <a id="features-classes"></a></p> |
| |
| <h3 id="classes" class="numbered">Classes</h3> |
| |
| <h4 id="class-declarations" class="numbered">Class declarations</h4> |
| |
| <p>Class declarations <em>must not</em> be terminated with semicolons:</p> |
| |
| <pre><code class="language-ts good">class Foo { |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">class Foo { |
| }; // Unnecessary semicolon |
| </code></pre> |
| |
| <p>In contrast, statements that contain class expressions <em>must</em> be terminated with |
| a semicolon:</p> |
| |
| <pre><code class="language-ts good">export const Baz = class extends Bar { |
| method(): number { |
| return this.x; |
| } |
| }; // Semicolon here as this is a statement, not a declaration |
| </code></pre> |
| |
| <pre><code class="language-ts bad">exports const Baz = class extends Bar { |
| method(): number { |
| return this.x; |
| } |
| } |
| </code></pre> |
| |
| <p>It is neither encouraged nor discouraged to have blank lines separating class |
| declaration braces from other class content:</p> |
| |
| <pre><code class="language-ts good">// No spaces around braces - fine. |
| class Baz { |
| method(): number { |
| return this.x; |
| } |
| } |
| |
| // A single space around both braces - also fine. |
| class Foo { |
| |
| method(): number { |
| return this.x; |
| } |
| |
| } |
| </code></pre> |
| |
| <h4 id="class-method-declarations" class="numbered">Class method declarations</h4> |
| |
| <p>Class method declarations <em>must not</em> use a semicolon to separate individual |
| method declarations:</p> |
| |
| <pre><code class="language-ts good">class Foo { |
| doThing() { |
| console.log("A"); |
| } |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">class Foo { |
| doThing() { |
| console.log("A"); |
| }; // <-- unnecessary |
| } |
| </code></pre> |
| |
| <p>Method declarations should be separated from surrounding code by a single blank |
| line:</p> |
| |
| <pre><code class="language-ts good">class Foo { |
| doThing() { |
| console.log("A"); |
| } |
| |
| getOtherThing(): number { |
| return 4; |
| } |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">class Foo { |
| doThing() { |
| console.log("A"); |
| } |
| getOtherThing(): number { |
| return 4; |
| } |
| } |
| </code></pre> |
| |
| <p><a id="features-classes-overriding-tostring"></a></p> |
| |
| <h5 id="overriding-tostring" class="numbered">Overriding toString</h5> |
| |
| <p>The <code>toString</code> method may be overridden, but must always succeed and never have |
| visible side effects.</p> |
| |
| <p>Tip: Beware, in particular, of calling other methods from toString, since |
| exceptional conditions could lead to infinite loops.</p> |
| |
| <p><a id="features-classes-static-methods"></a></p> |
| |
| <h4 id="static-methods" class="numbered">Static methods</h4> |
| |
| <h5 id="avoid-private-static-methods" class="numbered">Avoid private static methods</h5> |
| |
| <p>Where it does not interfere with readability, prefer module-local functions over |
| private static methods.</p> |
| |
| <h5 id="avoid-static-method-dynamic-dispatch" class="numbered">Do not rely on dynamic dispatch</h5> |
| |
| <p>Code <em>should not</em> rely on dynamic dispatch of static |
| methods. Static methods <em>should</em> only be called on the base class |
| itself (which defines it directly). Static methods <em>should not</em> be called on |
| variables containing a dynamic instance that may be either the constructor or a |
| subclass constructor (and <em>must</em> be defined with <code>@nocollapse</code> if this is done), |
| and <em>must not</em> be called directly on a subclass that doesn’t define the method |
| itself.</p> |
| |
| <p>Disallowed:</p> |
| |
| <pre><code class="language-ts bad">// Context for the examples below (this class is okay by itself) |
| class Base { |
| /** @nocollapse */ static foo() {} |
| } |
| class Sub extends Base {} |
| |
| // Discouraged: don't call static methods dynamically |
| function callFoo(cls: typeof Base) { |
| cls.foo(); |
| } |
| |
| // Disallowed: don't call static methods on subclasses that don't define it themselves |
| Sub.foo(); |
| |
| // Disallowed: don't access this in static methods. |
| class MyClass { |
| static foo() { |
| return this.staticField; |
| } |
| } |
| MyClass.staticField = 1; |
| </code></pre> |
| |
| <h5 id="static-this" class="numbered">Avoid static <code>this</code> references</h5> |
| |
| <p>Code <em>must not</em> use <code>this</code> in a static context.</p> |
| |
| <p>JavaScript allows accessing static fields through <code>this</code>. Different from other |
| languages, static fields are also inherited.</p> |
| |
| <pre><code class="bad">class ShoeStore { |
| static storage: Storage = ...; |
| |
| static isAvailable(s: Shoe) { |
| // Bad: do not use `this` in a static method. |
| return this.storage.has(s.id); |
| } |
| } |
| |
| class EmptyShoeStore extends ShoeStore { |
| static storage: Storage = EMPTY_STORE; // overrides storage from ShoeStore |
| } |
| </code></pre> |
| |
| <section class="zippy"> |
| |
| <p>Why?</p> |
| |
| <p>This code is generally surprising: authors might not expect that static fields |
| can be accessed through the this pointer, and might be surprised to find that |
| they can be overridden - this feature is not commonly used.</p> |
| |
| <p>This code also encourages an anti-pattern of having substantial static state, |
| which causes problems with testability.</p> |
| |
| </section> |
| |
| <p><a id="disallowed-features-omitting-parents-with-new"></a> |
| <a id="features-classes-constructors"></a></p> |
| |
| <h4 id="constructors" class="numbered">Constructors</h4> |
| |
| <p>Constructor calls <em>must</em> use parentheses, even when no arguments are passed:</p> |
| |
| <pre><code class="language-ts bad">const x = new Foo; |
| </code></pre> |
| |
| <pre><code class="language-ts good">const x = new Foo(); |
| </code></pre> |
| |
| <p>Omitting parentheses can lead to subtle mistakes. These two lines are not |
| equivalent:</p> |
| |
| <pre><code class="language-js good">new Foo().Bar(); |
| new Foo.Bar(); |
| </code></pre> |
| |
| <p>It is unnecessary to provide an empty constructor or one that simply delegates |
| into its parent class because ES2015 provides a default class constructor if one |
| is not specified. However constructors with parameter properties, visibility |
| modifiers or parameter decorators <em>should not</em> be omitted even if the body of |
| the constructor is empty.</p> |
| |
| <pre><code class="language-ts bad">class UnnecessaryConstructor { |
| constructor() {} |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">class UnnecessaryConstructorOverride extends Base { |
| constructor(value: number) { |
| super(value); |
| } |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts good">class DefaultConstructor { |
| } |
| |
| class ParameterProperties { |
| constructor(private myService) {} |
| } |
| |
| class ParameterDecorators { |
| constructor(@SideEffectDecorator myService) {} |
| } |
| |
| class NoInstantiation { |
| private constructor() {} |
| } |
| </code></pre> |
| |
| <p>The constructor should be separated from surrounding code both above and below |
| by a single blank line:</p> |
| |
| <pre><code class="language-ts good">class Foo { |
| myField = 10; |
| |
| constructor(private readonly ctorParam) {} |
| |
| doThing() { |
| console.log(ctorParam.getThing() + myField); |
| } |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">class Foo { |
| myField = 10; |
| constructor(private readonly ctorParam) {} |
| doThing() { |
| console.log(ctorParam.getThing() + myField); |
| } |
| } |
| </code></pre> |
| |
| <h4 id="class-members" class="numbered">Class members</h4> |
| |
| <h5 id="private-fields" class="numbered">No #private fields</h5> |
| |
| <p>Do not use private fields (also known as private identifiers):</p> |
| |
| <pre><code class="language-ts bad">class Clazz { |
| #ident = 1; |
| } |
| </code></pre> |
| |
| <p>Instead, use TypeScript's visibility annotations:</p> |
| |
| <pre><code class="language-ts good">class Clazz { |
| private ident = 1; |
| } |
| </code></pre> |
| |
| <section class="zippy"> |
| |
| <p>Why?</p> |
| |
| <p> Private identifiers cause substantial emit size and |
| performance regressions when down-leveled by TypeScript, and are unsupported |
| before ES2015. They can only be downleveled to ES2015, not lower. At the same |
| time, they do not offer substantial benefits when static type checking is used |
| to enforce visibility.</p> |
| |
| </section> |
| |
| <h5 id="use-readonly" class="numbered">Use readonly</h5> |
| |
| <p>Mark properties that are never reassigned outside of the constructor with the |
| <code>readonly</code> modifier (these need not be deeply immutable).</p> |
| |
| <h5 id="parameter-properties" class="numbered">Parameter properties</h5> |
| |
| <p>Rather than plumbing an obvious initializer through to a class member, use a |
| TypeScript |
| <a href="https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties">parameter property</a>.</p> |
| |
| <pre><code class="language-ts bad">class Foo { |
| private readonly barService: BarService; |
| |
| constructor(barService: BarService) { |
| this.barService = barService; |
| } |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts good">class Foo { |
| constructor(private readonly barService: BarService) {} |
| } |
| </code></pre> |
| |
| <p>If the parameter property needs documentation, |
| <a href="#parameter-property-comments">use an <code>@param</code> JSDoc tag</a>.</p> |
| |
| <h5 id="field-initializers" class="numbered">Field initializers</h5> |
| |
| <p>If a class member is not a parameter, initialize it where it's declared, which |
| sometimes lets you drop the constructor entirely.</p> |
| |
| <pre><code class="language-ts bad">class Foo { |
| private readonly userList: string[]; |
| |
| constructor() { |
| this.userList = []; |
| } |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts good">class Foo { |
| private readonly userList: string[] = []; |
| } |
| </code></pre> |
| |
| <p>Tip: Properties should never be added to or removed from an instance after the |
| constructor is finished, since it significantly hinders VMs’ ability to optimize |
| classes' <q>shape</q>. Optional fields that may be filled in later should be |
| explicitly initialized to <code>undefined</code> to prevent later shape changes.</p> |
| |
| <h5 id="properties-used-outside-of-class-lexical-scope" class="numbered">Properties used outside of class lexical scope</h5> |
| |
| <p>Properties used from outside the lexical scope of their containing class, such |
| as an Angular component's properties used from a template, <em>must not</em> use |
| <code>private</code> visibility, as they are used outside of the lexical scope of their |
| containing class.</p> |
| |
| <p>Use either <code>protected</code> or <code>public</code> as appropriate to the property in question. |
| Angular and AngularJS template properties should use <code>protected</code>, but Polymer |
| should use <code>public</code>.</p> |
| |
| <p>TypeScript code <em>must not</em> use <code>obj['foo']</code> to bypass the visibility of a |
| property.</p> |
| |
| <section class="zippy"> |
| |
| <p>Why?</p> |
| |
| <p>When a property is <code>private</code>, you are declaring to both automated systems and |
| humans that the property accesses are scoped to the methods of the declaring |
| class, and they will rely on that. For example, a check for unused code will |
| flag a private property that appears to be unused, even if some other file |
| manages to bypass the visibility restriction.</p> |
| |
| <p>Though it might appear that <code>obj['foo']</code> can bypass visibility in the TypeScript |
| compiler, this pattern can be broken by rearranging the build rules, and also |
| violates <a href="#optimization-compatibility">optimization compatibility</a>.</p> |
| |
| </section> |
| |
| <p><a id="features-classes-getters-and-setters"></a></p> |
| |
| <h5 id="classes-getters-and-setters" class="numbered">Getters and setters</h5> |
| |
| |
| |
| <p>Getters and setters, also known as accessors, for class members <em>may</em> be used. |
| The getter method <em>must</em> be a |
| <a href="https://en.wikipedia.org/wiki/Pure_function">pure function</a> (i.e., result is |
| consistent and has no side effects: getters <em>must not</em> change observable state). |
| They are also useful as a means of restricting the visibility of internal or |
| verbose implementation details (shown below).</p> |
| |
| <pre><code class="language-ts good">class Foo { |
| constructor(private readonly someService: SomeService) {} |
| |
| get someMember(): string { |
| return this.someService.someVariable; |
| } |
| |
| set someMember(newValue: string) { |
| this.someService.someVariable = newValue; |
| } |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">class Foo { |
| nextId = 0; |
| get next() { |
| return this.nextId++; // Bad: getter changes observable state |
| } |
| } |
| </code></pre> |
| |
| <p>If an accessor is used to hide a class property, the hidden property <em>may</em> be |
| prefixed or suffixed with any whole word, like <code>internal</code> or <code>wrapped</code>. When |
| using these private properties, access the value through the accessor whenever |
| possible. At least one accessor for a property <em>must</em> be non-trivial: do not |
| define <q>pass-through</q> accessors only for the purpose of hiding a property. |
| Instead, make the property public (or consider making it <code>readonly</code> rather than |
| just defining a getter with no setter).</p> |
| |
| <pre><code class="language-ts good">class Foo { |
| private wrappedBar = ''; |
| get bar() { |
| return this.wrappedBar || 'bar'; |
| } |
| |
| set bar(wrapped: string) { |
| this.wrappedBar = wrapped.trim(); |
| } |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">class Bar { |
| private barInternal = ''; |
| // Neither of these accessors have logic, so just make bar public. |
| get bar() { |
| return this.barInternal; |
| } |
| |
| set bar(value: string) { |
| this.barInternal = value; |
| } |
| } |
| </code></pre> |
| |
| <p>Getters and setters <em>must not</em> be defined using <code>Object.defineProperty</code>, since |
| this interferes with property renaming.</p> |
| |
| <p><a id="features-classes-computed-properties"></a></p> |
| |
| <h5 id="class-computed-properties" class="numbered">Computed properties</h5> |
| |
| <p>Computed properties may only be used in classes when the property is a symbol. |
| Dict-style properties (that is, quoted or computed non-symbol keys) are not |
| allowed (see |
| <a href="#features-objects-mixing-keys">rationale for not mixing key types</a>. A |
| <code>[Symbol.iterator]</code> method should be defined for any classes that are logically |
| iterable. Beyond this, <code>Symbol</code> should be used sparingly.</p> |
| |
| <p>Tip: be careful of using any other built-in symbols (e.g. |
| <code>Symbol.isConcatSpreadable</code>) as they are not polyfilled by the compiler and will |
| therefore not work in older browsers.</p> |
| |
| |
| |
| <h4 id="visibility" class="numbered">Visibility</h4> |
| |
| <p>Restricting visibility of properties, methods, and entire types helps with |
| keeping code decoupled.</p> |
| |
| <ul> |
| <li>Limit symbol visibility as much as possible.</li> |
| <li>Consider converting private methods to non-exported functions within the |
| same file but outside of any class, and moving private properties into a |
| separate, non-exported class.</li> |
| <li>TypeScript symbols are public by default. Never use the <code>public</code> modifier |
| except when declaring non-readonly public parameter properties (in |
| constructors).</li> |
| </ul> |
| |
| <pre><code class="language-ts bad">class Foo { |
| public bar = new Bar(); // BAD: public modifier not needed |
| |
| constructor(public readonly baz: Baz) {} // BAD: readonly implies it's a property which defaults to public |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts good">class Foo { |
| bar = new Bar(); // GOOD: public modifier not needed |
| |
| constructor(public baz: Baz) {} // public modifier allowed |
| } |
| </code></pre> |
| |
| <p>See also <a href="#export-visibility">export visibility</a>.</p> |
| |
| <h4 id="disallowed-class-patterns" class="numbered">Disallowed class patterns</h4> |
| |
| <p><a id="features-classes-prototypes"></a></p> |
| |
| <h5 id="class-prototypes" class="numbered">Do not manipulate <code>prototype</code>s directly</h5> |
| |
| <p>The <code>class</code> keyword allows clearer and more readable class definitions than |
| defining <code>prototype</code> properties. Ordinary implementation code has no business |
| manipulating these objects. Mixins and modifying the prototypes of builtin |
| objects are explicitly forbidden.</p> |
| |
| <p><strong>Exception</strong>: Framework code (such as Polymer, or Angular) may need to use <code>prototype</code>s, and should not resort |
| to even-worse workarounds to avoid doing so.</p> |
| |
| |
| |
| |
| |
| <p><a id="features-functions"></a></p> |
| |
| <h3 id="functions" class="numbered">Functions</h3> |
| |
| <h4 id="terminology" class="numbered">Terminology</h4> |
| |
| <p>There are many different types of functions, with subtle distinctions between |
| them. This guide uses the following terminology, which aligns with |
| <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions">MDN</a>:</p> |
| |
| <ul> |
| <li><q>function declaration</q>: a declaration (i.e. not an expression) using the |
| <code>function</code> keyword</li> |
| <li><q>function expression</q>: an expression, typically used in an assignment or |
| passed as a parameter, using the <code>function</code> keyword</li> |
| <li><q>arrow function</q>: an expression using the <code>=></code> syntax</li> |
| <li><q>block body</q>: right hand side of an arrow function with braces</li> |
| <li><q>concise body</q>: right hand side of an arrow function without braces</li> |
| </ul> |
| |
| <p>Methods and classes/constructors are not covered in this section.</p> |
| |
| <h4 id="function-declarations" class="numbered">Prefer function declarations for named functions</h4> |
| |
| <p>Prefer function declarations over arrow functions or function expressions when |
| defining named functions.</p> |
| |
| <pre><code class="language-.ts good">function foo() { |
| return 42; |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">const foo = () => 42; |
| </code></pre> |
| |
| <p>Arrow functions <em>may</em> be used, for example, when an explicit type annotation is |
| required.</p> |
| |
| <pre><code class="language-ts good">interface SearchFunction { |
| (source: string, subString: string): boolean; |
| } |
| |
| const fooSearch: SearchFunction = (source, subString) => { ... }; |
| </code></pre> |
| |
| |
| |
| |
| |
| <p><a id="features-functions-nested-functions"></a></p> |
| |
| <h4 id="nested-functions" class="numbered">Nested functions</h4> |
| |
| <p>Functions nested within other methods or functions <em>may</em> use function |
| declarations or arrow functions, as appropriate. In method bodies in particular, |
| arrow functions are preferred because they have access to the outer <code>this</code>.</p> |
| |
| <p><a id="use-arrow-functions-in-expressions"></a></p> |
| |
| <h4 id="function-expressions" class="numbered">Do not use function expressions</h4> |
| |
| <p>Do not use function expressions. Use arrow functions instead.</p> |
| |
| <pre><code class="language-ts good">bar(() => { this.doSomething(); }) |
| </code></pre> |
| |
| <pre><code class="language-ts bad">bar(function() { ... }) |
| </code></pre> |
| |
| <p><strong>Exception:</strong> Function expressions <em>may</em> be used <em>only if</em> code has to |
| dynamically rebind <code>this</code> (but this is <a href="#rebinding-this">discouraged</a>), or for |
| generator functions (which do not have an arrow syntax).</p> |
| |
| <p><a id="features-functions-arrow-functions"></a> |
| <a id="expression-bodies-vs-block-bodies"></a></p> |
| |
| <h4 id="arrow-function-bodies" class="numbered">Arrow function bodies</h4> |
| |
| <p>Use arrow functions with concise bodies (i.e. expressions) or block bodies as |
| appropriate.</p> |
| |
| <pre><code class="language-ts good">// Top level functions use function declarations. |
| function someFunction() { |
| // Block bodies are fine: |
| const receipts = books.map((b: Book) => { |
| const receipt = payMoney(b.price); |
| recordTransaction(receipt); |
| return receipt; |
| }); |
| |
| // Concise bodies are fine, too, if the return value is used: |
| const longThings = myValues.filter(v => v.length > 1000).map(v => String(v)); |
| |
| function payMoney(amount: number) { |
| // function declarations are fine, but must not access `this`. |
| } |
| |
| // Nested arrow functions may be assigned to a const. |
| const computeTax = (amount: number) => amount * 0.12; |
| } |
| </code></pre> |
| |
| <p>Only use a concise body if the return value of the function is actually used. |
| The block body makes sure the return type is <code>void</code> then and prevents potential |
| side effects.</p> |
| |
| <pre><code class="language-ts bad">// BAD: use a block body if the return value of the function is not used. |
| myPromise.then(v => console.log(v)); |
| // BAD: this typechecks, but the return value still leaks. |
| let f: () => void; |
| f = () => 1; |
| </code></pre> |
| |
| <pre><code class="language-ts good">// GOOD: return value is unused, use a block body. |
| myPromise.then(v => { |
| console.log(v); |
| }); |
| // GOOD: code may use blocks for readability. |
| const transformed = [1, 2, 3].map(v => { |
| const intermediate = someComplicatedExpr(v); |
| const more = acrossManyLines(intermediate); |
| return worthWrapping(more); |
| }); |
| // GOOD: explicit `void` ensures no leaked return value |
| myPromise.then(v => void console.log(v)); |
| </code></pre> |
| |
| <p>Tip: The <code>void</code> operator can be used to ensure an arrow function with an |
| expression body returns <code>undefined</code> when the result is unused.</p> |
| |
| <h4 id="rebinding-this" class="numbered">Rebinding <code>this</code></h4> |
| |
| <p>Function expressions and function declarations <em>must not</em> use <code>this</code> unless they |
| specifically exist to rebind the <code>this</code> pointer. Rebinding <code>this</code> can in most |
| cases be avoided by using arrow functions or explicit parameters.</p> |
| |
| <pre><code class="language-ts bad">function clickHandler() { |
| // Bad: what's `this` in this context? |
| this.textContent = 'Hello'; |
| } |
| // Bad: the `this` pointer reference is implicitly set to document.body. |
| document.body.onclick = clickHandler; |
| </code></pre> |
| |
| <pre><code class="language-ts good">// Good: explicitly reference the object from an arrow function. |
| document.body.onclick = () => { document.body.textContent = 'hello'; }; |
| // Alternatively: take an explicit parameter |
| const setTextFn = (e: HTMLElement) => { e.textContent = 'hello'; }; |
| document.body.onclick = setTextFn.bind(null, document.body); |
| </code></pre> |
| |
| <p>Prefer arrow functions over other approaches to binding <code>this</code>, such as |
| <code>f.bind(this)</code>, <code>goog.bind(f, this)</code>, or <code>const self = this</code>.</p> |
| |
| <h4 id="functions-as-callbacks" class="numbered">Prefer passing arrow functions as callbacks</h4> |
| |
| <p>Callbacks can be invoked with unexpected arguments that can pass a type check |
| but still result in logical errors.</p> |
| |
| <p>Avoid passing a named callback to a higher-order function, unless you are sure |
| of the stability of both functions' call signatures. Beware, in particular, of |
| less-commonly-used optional parameters.</p> |
| |
| <pre><code class="language-ts bad">// BAD: Arguments are not explicitly passed, leading to unintended behavior |
| // when the optional `radix` argument gets the array indices 0, 1, and 2. |
| const numbers = ['11', '5', '10'].map(parseInt); |
| // > [11, NaN, 2]; |
| </code></pre> |
| |
| <p>Instead, prefer passing an arrow-function that explicitly forwards parameters to |
| the named callback.</p> |
| |
| <pre><code class="language-ts good">// GOOD: Arguments are explicitly passed to the callback |
| const numbers = ['11', '5', '3'].map((n) => parseInt(n)); |
| // > [11, 5, 3] |
| |
| // GOOD: Function is locally defined and is designed to be used as a callback |
| function dayFilter(element: string|null|undefined) { |
| return element != null && element.endsWith('day'); |
| } |
| |
| const days = ['tuesday', undefined, 'juice', 'wednesday'].filter(dayFilter); |
| </code></pre> |
| |
| <h4 id="arrow-functions-as-properties" class="numbered">Arrow functions as properties</h4> |
| |
| <p>Classes usually <em>should not</em> contain properties initialized to arrow functions. |
| Arrow function properties require the calling function to understand that the |
| callee's <code>this</code> is already bound, which increases confusion about what <code>this</code> |
| is, and call sites and references using such handlers look broken (i.e. require |
| non-local knowledge to determine that they are correct). Code <em>should</em> always |
| use arrow functions to call instance methods (<code>const handler = (x) => { |
| this.listener(x); };</code>), and <em>should not</em> obtain or pass references to instance |
| methods (<del><code>const handler = this.listener; handler(x);</code></del>).</p> |
| |
| <blockquote> |
| <p>Note: in some specific situations, e.g. when binding functions in a template, |
| arrow functions as properties are useful and create much more readable code. |
| Use judgement with this rule. Also, see the |
| <a href="#event-handlers"><code>Event Handlers</code></a> section below.</p> |
| </blockquote> |
| |
| <pre><code class="language-ts bad">class DelayHandler { |
| constructor() { |
| // Problem: `this` is not preserved in the callback. `this` in the callback |
| // will not be an instance of DelayHandler. |
| setTimeout(this.patienceTracker, 5000); |
| } |
| private patienceTracker() { |
| this.waitedPatiently = true; |
| } |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">// Arrow functions usually should not be properties. |
| class DelayHandler { |
| constructor() { |
| // Bad: this code looks like it forgot to bind `this`. |
| setTimeout(this.patienceTracker, 5000); |
| } |
| private patienceTracker = () => { |
| this.waitedPatiently = true; |
| } |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts good">// Explicitly manage `this` at call time. |
| class DelayHandler { |
| constructor() { |
| // Use anonymous functions if possible. |
| setTimeout(() => { |
| this.patienceTracker(); |
| }, 5000); |
| } |
| private patienceTracker() { |
| this.waitedPatiently = true; |
| } |
| } |
| </code></pre> |
| |
| <h4 id="event-handlers" class="numbered">Event handlers</h4> |
| |
| <p>Event handlers <em>may</em> use arrow functions when there is no need to uninstall the |
| handler (for example, if the event is emitted by the class itself). If the |
| handler requires uninstallation, arrow function properties are the right |
| approach, because they automatically capture <code>this</code> and provide a stable |
| reference to uninstall.</p> |
| |
| <pre><code class="language-ts good">// Event handlers may be anonymous functions or arrow function properties. |
| class Component { |
| onAttached() { |
| // The event is emitted by this class, no need to uninstall. |
| this.addEventListener('click', () => { |
| this.listener(); |
| }); |
| // this.listener is a stable reference, we can uninstall it later. |
| window.addEventListener('onbeforeunload', this.listener); |
| } |
| onDetached() { |
| // The event is emitted by window. If we don't uninstall, this.listener will |
| // keep a reference to `this` because it's bound, causing a memory leak. |
| window.removeEventListener('onbeforeunload', this.listener); |
| } |
| // An arrow function stored in a property is bound to `this` automatically. |
| private listener = () => { |
| confirm('Do you want to exit the page?'); |
| } |
| } |
| </code></pre> |
| |
| <p>Do not use <code>bind</code> in the expression that installs an event handler, because it |
| creates a temporary reference that can't be uninstalled.</p> |
| |
| <pre><code class="language-ts bad">// Binding listeners creates a temporary reference that prevents uninstalling. |
| class Component { |
| onAttached() { |
| // This creates a temporary reference that we won't be able to uninstall |
| window.addEventListener('onbeforeunload', this.listener.bind(this)); |
| } |
| onDetached() { |
| // This bind creates a different reference, so this line does nothing. |
| window.removeEventListener('onbeforeunload', this.listener.bind(this)); |
| } |
| private listener() { |
| confirm('Do you want to exit the page?'); |
| } |
| } |
| </code></pre> |
| |
| <p><a id="features-functions-default-parameters"></a></p> |
| |
| <h4 id="parameter-initializers" class="numbered">Parameter initializers</h4> |
| |
| <p>Optional function parameters <em>may</em> be given a default initializer to use when |
| the argument is omitted. Initializers <em>must not</em> have any observable side |
| effects. Initializers <em>should</em> be kept as simple as possible.</p> |
| |
| <pre><code class="language-ts good">function process(name: string, extraContext: string[] = []) {} |
| function activate(index = 0) {} |
| </code></pre> |
| |
| <pre><code class="language-ts bad">// BAD: side effect of incrementing the counter |
| let globalCounter = 0; |
| function newId(index = globalCounter++) {} |
| |
| // BAD: exposes shared mutable state, which can introduce unintended coupling |
| // between function calls |
| class Foo { |
| private readonly defaultPaths: string[]; |
| frobnicate(paths = defaultPaths) {} |
| } |
| </code></pre> |
| |
| <p>Use default parameters sparingly. Prefer |
| <a href="#features-objects-destructuring">destructuring</a> to create readable APIs when |
| there are more than a small handful of optional parameters that do not have a |
| natural order.</p> |
| |
| |
| |
| <p><a id="features-functions-rest-parameters"></a> |
| <a id="features-functions-spread-operator"></a></p> |
| |
| <h4 id="rest-and-spread" class="numbered">Prefer rest and spread when appropriate</h4> |
| |
| <p>Use a <em>rest</em> parameter instead of accessing <code>arguments</code>. Never name a local |
| variable or parameter <code>arguments</code>, which confusingly shadows the built-in name.</p> |
| |
| <pre><code class="language-ts good">function variadic(array: string[], ...numbers: number[]) {} |
| </code></pre> |
| |
| |
| |
| <p>Use function spread syntax instead of <code>Function.prototype.apply</code>.</p> |
| |
| <p><a id="features-functions-generators"></a></p> |
| |
| <h4 id="formatting-functions" class="numbered">Formatting functions</h4> |
| |
| <p>Blank lines at the start or end of the function body are not allowed.</p> |
| |
| <p>A single blank line <em>may</em> be used within function bodies sparingly to create |
| <em>logical groupings</em> of statements.</p> |
| |
| <p>Generators should attach the <code>*</code> to the <code>function</code> and <code>yield</code> keywords, as in |
| <code>function* foo()</code> and <code>yield* iter</code>, rather than <del><code>function *foo()</code></del> or |
| <del><code>yield *iter</code></del>.</p> |
| |
| <p>Parentheses around the left-hand side of a single-argument arrow function are |
| recommended but not required.</p> |
| |
| <p>Do not put a space after the <code>...</code> in rest or spread syntax.</p> |
| |
| <pre><code class="language-ts good">function myFunction(...elements: number[]) {} |
| myFunction(...array, ...iterable, ...generator()); |
| </code></pre> |
| |
| <h3 id="features-this" class="numbered">this</h3> |
| |
| <p>Only use <code>this</code> in class constructors and methods, functions that have an |
| explicit <code>this</code> type declared (e.g. <code>function func(this: ThisType, ...)</code>), or in |
| arrow functions defined in a scope where <code>this</code> may be used.</p> |
| |
| <p>Never use <code>this</code> to refer to the global object, the context of an <code>eval</code>, the |
| target of an event, or unnecessarily <code>call()</code>ed or <code>apply()</code>ed functions.</p> |
| |
| <pre><code class="language-ts bad">this.alert('Hello'); |
| </code></pre> |
| |
| <h3 id="interfaces" class="numbered">Interfaces</h3> |
| |
| |
| |
| |
| |
| <h3 id="primitive-literals" class="numbered">Primitive literals</h3> |
| |
| <p><a id="features-string-literals"></a></p> |
| |
| <h4 id="string-literals" class="numbered">String literals</h4> |
| |
| <p><a id="features-strings-use-single-quotes"></a></p> |
| |
| <h5 id="use-single-quotes" class="numbered">Use single quotes</h5> |
| |
| <p>Ordinary string literals are delimited with single quotes (<code>'</code>), rather than |
| double quotes (<code>"</code>).</p> |
| |
| <p>Tip: if a string contains a single quote character, consider using a template |
| string to avoid having to escape the quote.</p> |
| |
| <p><a id="syntax-no-line-continuations"></a> |
| <a id="features-strings-no-line-continuations"></a></p> |
| |
| <h5 id="no-line-continuations" class="numbered">No line continuations</h5> |
| |
| <p>Do not use <em>line continuations</em> (that is, ending a line inside a string literal |
| with a backslash) in either ordinary or template string literals. Even though |
| ES5 allows this, it can lead to tricky errors if any trailing whitespace comes |
| after the slash, and is less obvious to readers.</p> |
| |
| <p>Disallowed:</p> |
| |
| <pre><code class="language-ts bad">const LONG_STRING = 'This is a very very very very very very very long string. \ |
| It inadvertently contains long stretches of spaces due to how the \ |
| continued lines are indented.'; |
| </code></pre> |
| |
| <p>Instead, write</p> |
| |
| <pre><code class="language-ts good">const LONG_STRING = 'This is a very very very very very very long string. ' + |
| 'It does not contain long stretches of spaces because it uses ' + |
| 'concatenated strings.'; |
| const SINGLE_STRING = |
| 'http://it.is.also/acceptable_to_use_a_single_long_string_when_breaking_would_hinder_search_discoverability'; |
| </code></pre> |
| |
| <p><a id="features-strings-template-strings"></a></p> |
| |
| <h5 id="template-literals" class="numbered">Template literals</h5> |
| |
| <p>Use template literals (delimited with <code>`</code>) over complex string |
| concatenation, particularly if multiple string literals are involved. Template |
| literals may span multiple lines.</p> |
| |
| <p>If a template literal spans multiple lines, it does not need to follow the |
| indentation of the enclosing block, though it may if the added whitespace does |
| not matter.</p> |
| |
| <p>Example:</p> |
| |
| <pre><code class="language-ts good">function arithmetic(a: number, b: number) { |
| return `Here is a table of arithmetic operations: |
| ${a} + ${b} = ${a + b} |
| ${a} - ${b} = ${a - b} |
| ${a} * ${b} = ${a * b} |
| ${a} / ${b} = ${a / b}`; |
| } |
| </code></pre> |
| |
| <p><a id="features-number-literals"></a></p> |
| |
| <h4 id="number-literals" class="numbered">Number literals</h4> |
| |
| <p>Numbers may be specified in decimal, hex, octal, or binary. Use exactly <code>0x</code>, |
| <code>0o</code>, and <code>0b</code> prefixes, with lowercase letters, for hex, octal, and binary, |
| respectively. Never include a leading zero unless it is immediately followed by |
| <code>x</code>, <code>o</code>, or <code>b</code>.</p> |
| |
| <h4 id="type-coercion" class="numbered">Type coercion</h4> |
| |
| <p>TypeScript code <em>may</em> use the <code>String()</code> and <code>Boolean()</code> (note: no <code>new</code>!) |
| functions, string template literals, or <code>!!</code> to coerce types.</p> |
| |
| <pre><code class="language-ts good">const bool = Boolean(false); |
| const str = String(aNumber); |
| const bool2 = !!str; |
| const str2 = `result: ${bool2}`; |
| </code></pre> |
| |
| <p>Values of enum types (including unions of enum types and other types) <em>must not</em> |
| be converted to booleans with <code>Boolean()</code> or <code>!!</code>, and must instead be compared |
| explicitly with comparison operators.</p> |
| |
| <pre><code class="language-ts bad">enum SupportLevel { |
| NONE, |
| BASIC, |
| ADVANCED, |
| } |
| |
| const level: SupportLevel = ...; |
| let enabled = Boolean(level); |
| |
| const maybeLevel: SupportLevel|undefined = ...; |
| enabled = !!maybeLevel; |
| </code></pre> |
| |
| <pre><code class="language-ts good">enum SupportLevel { |
| NONE, |
| BASIC, |
| ADVANCED, |
| } |
| |
| const level: SupportLevel = ...; |
| let enabled = level !== SupportLevel.NONE; |
| |
| const maybeLevel: SupportLevel|undefined = ...; |
| enabled = level !== undefined && level !== SupportLevel.NONE; |
| </code></pre> |
| |
| <section class="zippy"> |
| |
| <p>Why?</p> |
| |
| <p>For most purposes, it doesn't matter what number or string value an enum name is |
| mapped to at runtime, because values of enum types are referred to by name in |
| source code. Consequently, engineers are accustomed to not thinking about this, |
| and so situations where it <em>does</em> matter are undesirable because they will be |
| surprising. Such is the case with conversion of enums to booleans; in |
| particular, by default, the first declared enum value is falsy (because it is 0) |
| while the others are truthy, which is likely to be unexpected. Readers of code |
| that uses an enum value may not even know whether it's the first declared value |
| or not.</p> |
| |
| </section> |
| |
| <p>Using string concatenation to cast to string is discouraged, as we check that |
| operands to the plus operator are of matching types.</p> |
| |
| <p>Code <em>must</em> use <code>Number()</code> to parse numeric values, and <em>must</em> check its return |
| for <code>NaN</code> values explicitly, unless failing to parse is impossible from context.</p> |
| |
| <p>Note: <code>Number('')</code>, <code>Number(' ')</code>, and <code>Number('\t')</code> would return <code>0</code> instead |
| of <code>NaN</code>. <code>Number('Infinity')</code> and <code>Number('-Infinity')</code> would return <code>Infinity</code> |
| and <code>-Infinity</code> respectively. Additionally, exponential notation such as |
| <code>Number('1e+309')</code> and <code>Number('-1e+309')</code> can overflow into <code>Infinity</code>. These |
| cases may require special handling.</p> |
| |
| <pre><code class="language-ts good">const aNumber = Number('123'); |
| if (!isFinite(aNumber)) throw new Error(...); |
| </code></pre> |
| |
| |
| |
| <p>Code <em>must not</em> use unary plus (<code>+</code>) to coerce strings to numbers. Parsing |
| numbers can fail, has surprising corner cases, and can be a code smell (parsing |
| at the wrong layer). A unary plus is too easy to miss in code reviews given |
| this.</p> |
| |
| <pre><code class="language-ts bad">const x = +y; |
| </code></pre> |
| |
| <p>Code also <em>must not</em> use <code>parseInt</code> or <code>parseFloat</code> to parse numbers, except for |
| non-base-10 strings (see below). Both of those functions ignore trailing |
| characters in the string, which can shadow error conditions (e.g. parsing <code>12 |
| dwarves</code> as <code>12</code>).</p> |
| |
| <pre><code class="language-ts bad">const n = parseInt(someString, 10); // Error prone, |
| const f = parseFloat(someString); // regardless of passing a radix. |
| </code></pre> |
| |
| <p>Code that requires parsing with a radix <em>must</em> check that its input contains |
| only appropriate digits for that radix before calling into <code>parseInt</code>;</p> |
| |
| <pre><code class="language-ts good">if (!/^[a-fA-F0-9]+$/.test(someString)) throw new Error(...); |
| // Needed to parse hexadecimal. |
| // tslint:disable-next-line:ban |
| const n = parseInt(someString, 16); // Only allowed for radix != 10 |
| </code></pre> |
| |
| <p>Use <code>Number()</code> followed by <code>Math.floor</code> or <code>Math.trunc</code> (where available) to |
| parse integer numbers:</p> |
| |
| <pre><code class="language-ts good">let f = Number(someString); |
| if (isNaN(f)) handleError(); |
| f = Math.floor(f); |
| </code></pre> |
| |
| <p><a id="type-coercion-implicit"></a></p> |
| |
| <h5 id="implicit-coercion" class="numbered">Implicit coercion</h5> |
| |
| <p>Do not use explicit boolean coercions in conditional clauses that have implicit |
| boolean coercion. Those are the conditions in an <code>if</code>, <code>for</code> and <code>while</code> |
| statements.</p> |
| |
| <pre><code class="language-ts bad">const foo: MyInterface|null = ...; |
| if (!!foo) {...} |
| while (!!foo) {...} |
| </code></pre> |
| |
| <pre><code class="language-ts good">const foo: MyInterface|null = ...; |
| if (foo) {...} |
| while (foo) {...} |
| </code></pre> |
| |
| <p><a href="#type-coercion">As with explicit conversions</a>, values of enum types (including |
| unions of enum types and other types) <em>must not</em> be implicitly coerced to |
| booleans, and must instead be compared explicitly with comparison operators.</p> |
| |
| <pre><code class="language-ts bad">enum SupportLevel { |
| NONE, |
| BASIC, |
| ADVANCED, |
| } |
| |
| const level: SupportLevel = ...; |
| if (level) {...} |
| |
| const maybeLevel: SupportLevel|undefined = ...; |
| if (level) {...} |
| </code></pre> |
| |
| <pre><code class="language-ts good">enum SupportLevel { |
| NONE, |
| BASIC, |
| ADVANCED, |
| } |
| |
| const level: SupportLevel = ...; |
| if (level !== SupportLevel.NONE) {...} |
| |
| const maybeLevel: SupportLevel|undefined = ...; |
| if (level !== undefined && level !== SupportLevel.NONE) {...} |
| </code></pre> |
| |
| <p>Other types of values may be either implicitly coerced to booleans or compared |
| explicitly with comparison operators:</p> |
| |
| <pre><code class="language-ts good">// Explicitly comparing > 0 is OK: |
| if (arr.length > 0) {...} |
| // so is relying on boolean coercion: |
| if (arr.length) {...} |
| </code></pre> |
| |
| <p><a id="features-control-structures"></a></p> |
| |
| <h3 id="control-structures" class="numbered">Control structures</h3> |
| |
| <p><a id="formatting-braces"></a> |
| <a id="formatting-braces-all"></a></p> |
| |
| <h4 id="control-flow-statements-blocks" class="numbered">Control flow statements and blocks</h4> |
| |
| <p>Control flow statements (<code>if</code>, <code>else</code>, <code>for</code>, <code>do</code>, <code>while</code>, etc) always use |
| braced blocks for the containing code, even if the body contains only a single |
| statement. The first statement of a non-empty block must begin on its own line.</p> |
| |
| <pre><code class="language-ts good">for (let i = 0; i < x; i++) { |
| doSomethingWith(i); |
| } |
| |
| if (x) { |
| doSomethingWithALongMethodNameThatForcesANewLine(x); |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">if (x) |
| doSomethingWithALongMethodNameThatForcesANewLine(x); |
| |
| for (let i = 0; i < x; i++) doSomethingWith(i); |
| </code></pre> |
| |
| <p><strong>Exception:</strong> <code>if</code> statements fitting on one line <em>may</em> elide the block.</p> |
| |
| <pre><code class="language-ts good">if (x) x.doFoo(); |
| </code></pre> |
| |
| <h5 id="assignment-in-control-statements" class="numbered">Assignment in control statements</h5> |
| |
| <p>Prefer to avoid assignment of variables inside control statements. Assignment |
| can be easily mistaken for equality checks inside control statements.</p> |
| |
| <pre><code class="language-ts bad">if (x = someFunction()) { |
| // Assignment easily mistaken with equality check |
| // ... |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts good">x = someFunction(); |
| if (x) { |
| // ... |
| } |
| </code></pre> |
| |
| <p>In cases where assignment inside the control statement is preferred, enclose the |
| assignment in additional parenthesis to indicate it is intentional.</p> |
| |
| <pre><code class="language-ts">while ((x = someFunction())) { |
| // Double parenthesis shows assignment is intentional |
| // ... |
| } |
| |
| </code></pre> |
| |
| <p><a id="features-for-loops"></a></p> |
| |
| <h5 id="iterating-containers" class="numbered">Iterating containers</h5> |
| |
| <p>Prefer <code>for (... of someArr)</code> to iterate over arrays. <code>Array.prototype.forEach</code> and vanilla <code>for</code> |
| loops are also allowed:</p> |
| |
| <pre><code class="language-ts good">for (const x of someArr) { |
| // x is a value of someArr. |
| } |
| |
| for (let i = 0; i < someArr.length; i++) { |
| // Explicitly count if the index is needed, otherwise use the for/of form. |
| const x = someArr[i]; |
| // ... |
| } |
| for (const [i, x] of someArr.entries()) { |
| // Alternative version of the above. |
| } |
| </code></pre> |
| |
| <p><code>for</code>-<code>in</code> loops may only be used on dict-style objects (see |
| <a href="#optimization-compatibility-for-property-access">below</a> for more info). Do not |
| use <code>for (... in ...)</code> to iterate over arrays as it will counterintuitively give |
| the array's indices (as strings!), not values:</p> |
| |
| <pre><code class="language-ts bad">for (const x in someArray) { |
| // x is the index! |
| } |
| </code></pre> |
| |
| <p><code>Object.prototype.hasOwnProperty</code> should be used in <code>for</code>-<code>in</code> loops to exclude |
| unwanted prototype properties. Prefer <code>for</code>-<code>of</code> with <code>Object.keys</code>, |
| <code>Object.values</code>, or <code>Object.entries</code> over <code>for</code>-<code>in</code> when possible.</p> |
| |
| <pre><code class="language-ts good">for (const key in obj) { |
| if (!obj.hasOwnProperty(key)) continue; |
| doWork(key, obj[key]); |
| } |
| for (const key of Object.keys(obj)) { |
| doWork(key, obj[key]); |
| } |
| for (const value of Object.values(obj)) { |
| doWorkValOnly(value); |
| } |
| for (const [key, value] of Object.entries(obj)) { |
| doWork(key, value); |
| } |
| </code></pre> |
| |
| <p><a id="formatting-grouping-parentheses"></a></p> |
| |
| <h4 id="grouping-parentheses" class="numbered">Grouping parentheses</h4> |
| |
| <p>Optional grouping parentheses are omitted only when the author and reviewer |
| agree that there is no reasonable chance that the code will be misinterpreted |
| without them, nor would they have made the code easier to read. It is <em>not</em> |
| reasonable to assume that every reader has the entire operator precedence table |
| memorized.</p> |
| |
| <p>Do not use unnecessary parentheses around the entire expression following |
| <code>delete</code>, <code>typeof</code>, <code>void</code>, <code>return</code>, <code>throw</code>, <code>case</code>, <code>in</code>, <code>of</code>, or <code>yield</code>.</p> |
| |
| |
| |
| <p><a id="exceptions"></a> |
| <a id="features-exceptions"></a></p> |
| |
| <h4 id="exception-handling" class="numbered">Exception handling</h4> |
| |
| <p>Exceptions are an important part of the language and should be used whenever |
| exceptional cases occur.</p> |
| |
| <p>Custom exceptions provide a great way to convey additional error information |
| from functions. They should be defined and used wherever the native <code>Error</code> type |
| is insufficient.</p> |
| |
| <p>Prefer throwing exceptions over ad-hoc error-handling approaches (such as |
| passing an error container reference type, or returning an object with an error |
| property).</p> |
| |
| <h5 id="instantiate-errors-using-new" class="numbered">Instantiate errors using <code>new</code></h5> |
| |
| <p>Always use <code>new Error()</code> when instantiating exceptions, instead of just calling |
| <code>Error()</code>. Both forms create a new <code>Error</code> instance, but using <code>new</code> is more |
| consistent with how other objects are instantiated.</p> |
| |
| <pre><code class="language-ts good">throw new Error('Foo is not a valid bar.'); |
| </code></pre> |
| |
| <pre><code class="language-ts bad">throw Error('Foo is not a valid bar.'); |
| </code></pre> |
| |
| <h5 id="only-throw-errors" class="numbered">Only throw errors</h5> |
| |
| <p>JavaScript (and thus TypeScript) allow throwing or rejecting a Promise with |
| arbitrary values. However if the thrown or rejected value is not an <code>Error</code>, it |
| does not populate stack trace information, making debugging hard. This treatment |
| extends to <code>Promise</code> rejection values as <code>Promise.reject(obj)</code> is equivalent to |
| <code>throw obj;</code> in async functions.</p> |
| |
| <pre><code class="language-ts bad">// bad: does not get a stack trace. |
| throw 'oh noes!'; |
| // For promises |
| new Promise((resolve, reject) => void reject('oh noes!')); |
| Promise.reject(); |
| Promise.reject('oh noes!'); |
| </code></pre> |
| |
| <p>Instead, only throw (subclasses of) <code>Error</code>:</p> |
| |
| <pre><code class="language-ts good">// Throw only Errors |
| throw new Error('oh noes!'); |
| // ... or subtypes of Error. |
| class MyError extends Error {} |
| throw new MyError('my oh noes!'); |
| // For promises |
| new Promise((resolve) => resolve()); // No reject is OK. |
| new Promise((resolve, reject) => void reject(new Error('oh noes!'))); |
| Promise.reject(new Error('oh noes!')); |
| </code></pre> |
| |
| <h5 id="catching-and-rethrowing" class="numbered">Catching and rethrowing</h5> |
| |
| <p>When catching errors, code <em>should</em> assume that all thrown errors are instances |
| of <code>Error</code>.</p> |
| |
| |
| |
| <section> |
| |
| <pre><code class="language-ts good">function assertIsError(e: unknown): asserts e is Error { |
| if (!(e instanceof Error)) throw new Error("e is not an Error"); |
| } |
| |
| try { |
| doSomething(); |
| } catch (e: unknown) { |
| // All thrown errors must be Error subtypes. Do not handle |
| // other possible values unless you know they are thrown. |
| assertIsError(e); |
| displayError(e.message); |
| // or rethrow: |
| throw e; |
| } |
| </code></pre> |
| |
| </section> |
| |
| <p>Exception handlers <em>must not</em> defensively handle non-<code>Error</code> types unless the |
| called API is conclusively known to throw non-<code>Error</code>s in violation of the above |
| rule. In that case, a comment should be included to specifically identify where |
| the non-<code>Error</code>s originate.</p> |
| |
| <pre><code class="language-ts good">try { |
| badApiThrowingStrings(); |
| } catch (e: unknown) { |
| // Note: bad API throws strings instead of errors. |
| if (typeof e === 'string') { ... } |
| } |
| </code></pre> |
| |
| <section class="zippy"> |
| |
| <p>Why?</p> |
| |
| <p>Avoid |
| <a href="https://en.wikipedia.org/wiki/Defensive_programming#Offensive_programming">overly defensive programming</a>. |
| Repeating the same defenses against a problem that will not exist in most code |
| leads to boiler-plate code that is not useful.</p> |
| |
| </section> |
| |
| <p><a id="features-empty-catch-blocks"></a></p> |
| |
| <h5 id="empty-catch-blocks" class="numbered">Empty catch blocks</h5> |
| |
| <p>It is very rarely correct to do nothing in response to a caught exception. When |
| it truly is appropriate to take no action whatsoever in a catch block, the |
| reason this is justified is explained in a comment.</p> |
| |
| <pre><code class="language-ts good"> try { |
| return handleNumericResponse(response); |
| } catch (e: unknown) { |
| // Response is not numeric. Continue to handle as text. |
| } |
| return handleTextResponse(response); |
| </code></pre> |
| |
| <p>Disallowed:</p> |
| |
| <pre><code class="language-ts bad"> try { |
| shouldFail(); |
| fail('expected an error'); |
| } catch (expected: unknown) { |
| } |
| </code></pre> |
| |
| <p>Tip: Unlike in some other languages, patterns like the above simply don’t work |
| since this will catch the error thrown by <code>fail</code>. Use <code>assertThrows()</code> instead.</p> |
| |
| <p><a id="features-switch-statements"></a> |
| <a id="features-switch-default-case"></a></p> |
| |
| <h4 id="switch-statements" class="numbered">Switch statements</h4> |
| |
| <p>All <code>switch</code> statements <em>must</em> contain a <code>default</code> statement group, even if it |
| contains no code. The <code>default</code> statement group must be last.</p> |
| |
| <pre><code class="language-ts good">switch (x) { |
| case Y: |
| doSomethingElse(); |
| break; |
| default: |
| // nothing to do. |
| } |
| </code></pre> |
| |
| <p>Within a switch block, each statement group either terminates abruptly with a |
| <code>break</code>, a <code>return</code> statement, or by throwing an exception. Non-empty statement |
| groups (<code>case ...</code>) <em>must not</em> fall through (enforced by the compiler):</p> |
| |
| <pre><code class="language-ts bad">switch (x) { |
| case X: |
| doSomething(); |
| // fall through - not allowed! |
| case Y: |
| // ... |
| } |
| </code></pre> |
| |
| <p>Empty statement groups are allowed to fall through:</p> |
| |
| <pre><code class="language-ts good">switch (x) { |
| case X: |
| case Y: |
| doSomething(); |
| break; |
| default: // nothing to do. |
| } |
| </code></pre> |
| |
| |
| |
| <p><a id="features-equality-checks"></a> |
| <a id="features-equality-checks-exceptions"></a></p> |
| |
| <h4 id="equality-checks" class="numbered">Equality checks</h4> |
| |
| <p>Always use triple equals (<code>===</code>) and not equals (<code>!==</code>). The double equality |
| operators cause error prone type coercions that are hard to understand and |
| slower to implement for JavaScript Virtual Machines. See also the |
| <a href="https://dorey.github.io/JavaScript-Equality-Table/">JavaScript equality table</a>.</p> |
| |
| <pre><code class="language-ts bad">if (foo == 'bar' || baz != bam) { |
| // Hard to understand behaviour due to type coercion. |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts good">if (foo === 'bar' || baz !== bam) { |
| // All good here. |
| } |
| </code></pre> |
| |
| <p><strong>Exception:</strong> Comparisons to the literal <code>null</code> value <em>may</em> use the <code>==</code> and |
| <code>!=</code> operators to cover both <code>null</code> and <code>undefined</code> values.</p> |
| |
| <pre><code class="language-ts good">if (foo == null) { |
| // Will trigger when foo is null or undefined. |
| } |
| </code></pre> |
| |
| <h4 id="type-and-non-nullability-assertions" class="numbered">Type and non-nullability assertions</h4> |
| |
| <p>Type assertions (<code>x as SomeType</code>) and non-nullability assertions (<code>y!</code>) are |
| unsafe. Both only silence the TypeScript compiler, but do not insert any runtime |
| checks to match these assertions, so they can cause your program to crash at |
| runtime.</p> |
| |
| <p>Because of this, you <em>should not</em> use type and non-nullability assertions |
| without an obvious or explicit reason for doing so.</p> |
| |
| <p>Instead of the following:</p> |
| |
| <pre><code class="language-ts bad">(x as Foo).foo(); |
| |
| y!.bar(); |
| </code></pre> |
| |
| <p>When you want to assert a type or non-nullability the best answer is to |
| explicitly write a runtime check that performs that check.</p> |
| |
| <pre><code class="language-ts good">// assuming Foo is a class. |
| if (x instanceof Foo) { |
| x.foo(); |
| } |
| |
| if (y) { |
| y.bar(); |
| } |
| </code></pre> |
| |
| <p>Sometimes due to some local property of your code you can be sure that the |
| assertion form is safe. In those situations, you <em>should</em> add clarification to |
| explain why you are ok with the unsafe behavior:</p> |
| |
| <pre><code class="language-ts good">// x is a Foo, because ... |
| (x as Foo).foo(); |
| |
| // y cannot be null, because ... |
| y!.bar(); |
| </code></pre> |
| |
| <p>If the reasoning behind a type or non-nullability assertion is obvious, the |
| comments <em>may</em> not be necessary. For example, generated proto code is always |
| nullable, but perhaps it is well-known in the context of the code that certain |
| fields are always provided by the backend. Use your judgement.</p> |
| |
| <h5 id="type-assertions-syntax" class="numbered">Type assertion syntax</h5> |
| |
| <p>Type assertions <em>must</em> use the <code>as</code> syntax (as opposed to the angle brackets |
| syntax). This enforces parentheses around the assertion when accessing a member.</p> |
| |
| <pre><code class="language-ts bad">const x = (<Foo>z).length; |
| const y = <Foo>z.length; |
| </code></pre> |
| |
| <pre><code class="language-ts good">// z must be Foo because ... |
| const x = (z as Foo).length; |
| </code></pre> |
| |
| <h5 id="double-assertions" class="numbered">Double assertions</h5> |
| |
| <p>From the |
| <a href="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions">TypeScript handbook</a>, |
| TypeScript only allows type assertions which convert to a <em>more specific</em> or |
| <em>less specific</em> version of a type. Adding a type assertion (<code>x as Foo</code>) which |
| does not meet this criteria will give the error: <q>Conversion of type 'X' to type |
| 'Y' may be a mistake because neither type sufficiently overlaps with the other.</q></p> |
| |
| <p>If you are sure an assertion is safe, you can perform a <em>double assertion</em>. This |
| involves casting through <code>unknown</code> since it is less specific than all types.</p> |
| |
| <pre><code class="language-ts good">// x is a Foo here, because... |
| (x as unknown as Foo).fooMethod(); |
| </code></pre> |
| |
| <p>Use <code>unknown</code> (instead of <code>any</code> or <code>{}</code>) as the intermediate type.</p> |
| |
| <h5 id="type-assertions-and-object-literals" class="numbered">Type assertions and object literals</h5> |
| |
| <p>Use type annotations (<code>: Foo</code>) instead of type assertions (<code>as Foo</code>) to specify |
| the type of an object literal. This allows detecting refactoring bugs when the |
| fields of an interface change over time.</p> |
| |
| <pre><code class="language-ts bad">interface Foo { |
| bar: number; |
| baz?: string; // was "bam", but later renamed to "baz". |
| } |
| |
| const foo = { |
| bar: 123, |
| bam: 'abc', // no error! |
| } as Foo; |
| |
| function func() { |
| return { |
| bar: 123, |
| bam: 'abc', // no error! |
| } as Foo; |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts good">interface Foo { |
| bar: number; |
| baz?: string; |
| } |
| |
| const foo: Foo = { |
| bar: 123, |
| bam: 'abc', // complains about "bam" not being defined on Foo. |
| }; |
| |
| function func(): Foo { |
| return { |
| bar: 123, |
| bam: 'abc', // complains about "bam" not being defined on Foo. |
| }; |
| } |
| </code></pre> |
| |
| <h4 id="keep-try-blocks-focused" class="numbered">Keep try blocks focused</h4> |
| |
| <p>Limit the amount of code inside a try block, if this can be done without hurting |
| readability.</p> |
| |
| <pre><code class="language-ts bad">try { |
| const result = methodThatMayThrow(); |
| use(result); |
| } catch (error: unknown) { |
| // ... |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts good">let result; |
| try { |
| result = methodThatMayThrow(); |
| } catch (error: unknown) { |
| // ... |
| } |
| use(result); |
| </code></pre> |
| |
| <p>Moving the non-throwable lines out of the try/catch block helps the reader learn |
| which method throws exceptions. Some inline calls that do not throw exceptions |
| could stay inside because they might not be worth the extra complication of a |
| temporary variable.</p> |
| |
| <p><strong>Exception:</strong> There may be performance issues if try blocks are inside a loop. |
| Widening try blocks to cover a whole loop is ok.</p> |
| |
| <h3 id="decorators" class="numbered">Decorators</h3> |
| |
| <p>Decorators are syntax with an <code>@</code> prefix, like <code>@MyDecorator</code>.</p> |
| |
| <p>Do not define new decorators. Only use the decorators defined by |
| frameworks:</p> |
| |
| <ul> |
| <li>Angular (e.g. <code>@Component</code>, <code>@NgModule</code>, etc.)</li> |
| <li>Polymer (e.g. <code>@property</code>)</li> |
| </ul> |
| |
| |
| |
| <section> |
| |
| <p>Why?</p> |
| |
| <p>We generally want to avoid decorators, because they were an experimental feature |
| that have since diverged from the TC39 proposal and have known bugs that won't |
| be fixed.</p> |
| |
| </section> |
| |
| <p>When using decorators, the decorator <em>must</em> immediately precede the symbol it |
| decorates, with no empty lines between:</p> |
| |
| <pre><code class="language-ts">/** JSDoc comments go before decorators */ |
| @Component({...}) // Note: no empty line after the decorator. |
| class MyComp { |
| @Input() myField: string; // Decorators on fields may be on the same line... |
| |
| @Input() |
| myOtherField: string; // ... or wrap. |
| } |
| </code></pre> |
| |
| <h3 id="disallowed-features" class="numbered">Disallowed features</h3> |
| |
| <p><a id="disallowed-features-wrapper-objects"></a></p> |
| |
| <h4 id="primitive-types-wrapper-classes" class="numbered">Wrapper objects for primitive types</h4> |
| |
| <p>TypeScript code <em>must not</em> instantiate the wrapper classes for the primitive |
| types <code>String</code>, <code>Boolean</code>, and <code>Number</code>. Wrapper classes have surprising |
| behavior, such as <code>new Boolean(false)</code> evaluating to <code>true</code>.</p> |
| |
| <pre><code class="language-ts bad">const s = new String('hello'); |
| const b = new Boolean(false); |
| const n = new Number(5); |
| </code></pre> |
| |
| <p>The wrappers may be called as functions for coercing (which is preferred over |
| using <code>+</code> or concatenating the empty string) or creating symbols. See |
| <a href="#type-coercion">type coercion</a> for more information.</p> |
| |
| <p><a id="formatting-semicolons-are-required"></a> |
| <a id="disallowed-features-automatic-semicolon-insertion"></a></p> |
| |
| <h4 id="automatic-semicolon-insertion" class="numbered">Automatic Semicolon Insertion</h4> |
| |
| <p>Do not rely on Automatic Semicolon Insertion (ASI). Explicitly end all |
| statements using a semicolon. This prevents bugs due to incorrect semicolon |
| insertions and ensures compatibility with tools with limited ASI support (e.g. |
| clang-format).</p> |
| |
| <h4 id="enums" class="numbered">Const enums</h4> |
| |
| <p>Code <em>must not</em> use <code>const enum</code>; use plain <code>enum</code> instead.</p> |
| |
| <section class="zippy"> |
| |
| <p>Why?</p> |
| |
| <p>TypeScript enums already cannot be mutated; <code>const enum</code> is a separate language |
| feature related to optimization that makes the enum invisible to |
| JavaScript users of the module.</p> |
| |
| </section> |
| |
| <h4 id="debugger-statements" class="numbered">Debugger statements</h4> |
| |
| <p>Debugger statements <em>must not</em> be included in production code.</p> |
| |
| <pre><code class="language-ts bad">function debugMe() { |
| debugger; |
| } |
| </code></pre> |
| |
| <p><a id="disallowed-features-with"></a></p> |
| |
| <h4 id="diallowed-features-with" class="numbered"><code>with</code></h4> |
| |
| <p>Do not use the <code>with</code> keyword. It makes your code harder to understand and |
| <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with">has been banned in strict mode since ES5</a>.</p> |
| |
| <p><a id="disallowed-features-dynamic-code-evaluation"></a></p> |
| |
| <h4 id="dynamic-code-evaluation" class="numbered">Dynamic code evaluation</h4> |
| |
| <p>Do not use <code>eval</code> or the <code>Function(...string)</code> constructor (except for code |
| loaders). These features are potentially dangerous and simply do not work in |
| environments using strict |
| <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">Content Security Policies</a>.</p> |
| |
| <p><a id="disallowed-features-non-standard-features"></a></p> |
| |
| <h4 id="non-standard-features" class="numbered">Non-standard features</h4> |
| |
| <p>Do not use non-standard ECMAScript or Web Platform features.</p> |
| |
| <p>This includes:</p> |
| |
| <ul> |
| <li>Old features that have been marked deprecated or removed entirely from |
| ECMAScript / the Web Platform (see |
| <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Deprecated_and_obsolete_features">MDN</a>)</li> |
| <li>New ECMAScript features that are not yet standardized |
| <ul> |
| <li>Avoid using features that are in current TC39 working draft or currently |
| in the <a href="https://tc39.es/process-document/">proposal process</a></li> |
| <li>Use only ECMAScript features defined in the current ECMA-262 |
| specification</li> |
| </ul></li> |
| <li>Proposed but not-yet-complete web standards: |
| <ul> |
| <li>WHATWG proposals that have not completed the |
| <a href="https://whatwg.org/faq#adding-new-features">proposal process</a>.</li> |
| </ul></li> |
| <li>Non-standard language “extensions” (such as those provided by some external |
| transpilers)</li> |
| </ul> |
| |
| <p>Projects targeting specific JavaScript runtimes, such as latest-Chrome-only, |
| Chrome extensions, Node.JS, Electron, can obviously use those APIs. Use caution |
| when considering an API surface that is proprietary and only implemented in some |
| browsers; consider whether there is a common library that can abstract this API |
| surface away for you.</p> |
| |
| <p><a id="disallowed-features-modifying-builtin-objects"></a></p> |
| |
| <h4 id="modifying-builtin-objects" class="numbered">Modifying builtin objects</h4> |
| |
| <p>Never modify builtin types, either by adding methods to their constructors or to |
| their prototypes. Avoid depending on libraries that do |
| this.</p> |
| |
| <p>Do not add symbols to the global object unless absolutely necessary (e.g. |
| required by a third-party API).</p> |
| |
| <p><a id="syntax"></a> |
| <a id="naming-rules-common-to-all-identifiers"></a></p> |
| |
| <h2 id="naming" class="numbered">Naming</h2> |
| |
| <h3 id="identifiers" class="numbered">Identifiers</h3> |
| |
| <p>Identifiers <em>must</em> use only ASCII letters, digits, underscores (for constants |
| and structured test method names), and (rarely) the '$' sign.</p> |
| |
| <h4 id="naming-style" class="numbered">Naming style</h4> |
| |
| <p>TypeScript expresses information in types, so names <em>should not</em> be decorated |
| with information that is included in the type. (See also |
| <a href="https://testing.googleblog.com/2017/10/code-health-identifiernamingpostforworl.html">Testing Blog</a> |
| for more about what |
| not to include.)</p> |
| |
| <p>Some concrete examples of this rule:</p> |
| |
| <ul> |
| <li>Do not use trailing or leading underscores for private properties or |
| methods.</li> |
| <li>Do not use the <code>opt_</code> prefix for optional parameters. |
| <ul> |
| <li>For accessors, see <a href="#getters-and-setters-accessors">accessor rules</a> |
| below.</li> |
| </ul></li> |
| <li>Do not mark interfaces specially (<del><code>IMyInterface</code></del> or |
| <del><code>MyFooInterface</code></del>) unless it's idiomatic in its |
| environment. When |
| introducing an interface for a class, give it a name that expresses why the |
| interface exists in the first place (e.g. <code>class TodoItem</code> and <code>interface |
| TodoItemStorage</code> if the interface expresses the format used for |
| storage/serialization in JSON).</li> |
| <li>Suffixing <code>Observable</code>s with <code>$</code> is a common external convention and can |
| help resolve confusion regarding observable values vs concrete values. |
| Judgement on whether this is a useful convention is left up to individual |
| teams, but <em>should</em> be consistent within projects.</li> |
| </ul> |
| |
| <p><a id="identifiers-abbreviations"></a></p> |
| |
| <h4 id="descriptive-names" class="numbered">Descriptive names</h4> |
| |
| <p>Names <em>must</em> 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.</p> |
| |
| <ul> |
| <li><strong>Exception:</strong> Variables that are in scope for 10 lines or fewer, including |
| arguments that are <em>not</em> part of an exported API, <em>may</em> use short (e.g. |
| single letter) variable names.</li> |
| </ul> |
| |
| <pre><code class="language-ts good">// Good identifiers: |
| errorCount // No abbreviation. |
| dnsConnectionIndex // Most people know what "DNS" stands for. |
| referrerUrl // Ditto for "URL". |
| customerId // "Id" is both ubiquitous and unlikely to be misunderstood. |
| </code></pre> |
| |
| <pre><code class="language-ts bad">// Disallowed identifiers: |
| n // Meaningless. |
| nErr // Ambiguous abbreviation. |
| nCompConns // Ambiguous abbreviation. |
| wgcConnections // Only your group knows what this stands for. |
| pcReader // Lots of things can be abbreviated "pc". |
| cstmrId // Deletes internal letters. |
| kSecondsPerDay // Do not use Hungarian notation. |
| customerID // Incorrect camelcase of "ID". |
| </code></pre> |
| |
| <p><a id="naming-camel-case-defined"></a></p> |
| |
| <h4 id="camel-case" class="numbered">Camel case</h4> |
| |
| <p> |
| Treat abbreviations like acronyms in names as whole words, i.e. use |
| <code>loadHttpUrl</code>, not <del><code>loadHTTPURL</code></del>, unless required by a platform name (e.g. |
| <code>XMLHttpRequest</code>).</p> |
| |
| <h4 id="identifiers-dollar-sign" class="numbered">Dollar sign</h4> |
| |
| <p>Identifiers <em>should not</em> generally use <code>$</code>, except when required by naming |
| conventions for third party frameworks. <a href="#naming-style">See above</a> for more on |
| using <code>$</code> with <code>Observable</code> values.</p> |
| |
| <p><a id="naming-class-names"></a> |
| <a id="naming-method-names"></a> |
| <a id="naming-enum-names"></a> |
| <a id="naming-constant-names"></a> |
| <a id="naming-local-variable-names"></a> |
| <a id="naming-non-constant-field-names"></a> |
| <a id="naming-parameter-names"></a></p> |
| |
| <h3 id="naming-rules-by-identifier-type" class="numbered">Rules by identifier type</h3> |
| |
| <p>Most identifier names should follow the casing in the table below, based on the |
| identifier's type.</p> |
| |
| <table> |
| <thead> |
| <tr> |
| <th>Style</th> |
| <th>Category</th> |
| </tr> |
| </thead> |
| |
| <tbody> |
| <tr> |
| <td><code>UpperCamelCase</code> |
| |
| </td> |
| <td>class / interface / type / enum / decorator / type |
| parameters / component functions in TSX / JSXElement type |
| parameter</td> |
| </tr> |
| <tr> |
| <td><code>lowerCamelCase</code> |
| </td> |
| <td>variable / parameter / function / method / property / |
| module alias</td> |
| </tr> |
| <tr> |
| <td><code>CONSTANT_CASE</code> |
| </td> |
| <td>global constant values, including enum values. See |
| <a href="#identifiers-constants">Constants</a> below.</td> |
| </tr> |
| <tr> |
| <td><code>#ident</code></td> |
| <td>private identifiers are never used.</td> |
| </tr> |
| </tbody> |
| </table> |
| |
| <p><a id="naming-template-parameter-names"></a></p> |
| |
| <h4 id="identifiers-type-parameters" class="numbered">Type parameters</h4> |
| |
| <p>Type parameters, like in <code>Array<T></code>, <em>may</em> use a single upper case character |
| (<code>T</code>) or <code>UpperCamelCase</code>.</p> |
| |
| |
| |
| <h4 id="identifiers-test-names" class="numbered">Test names</h4> |
| |
| <p>Test method names inxUnit-style test frameworks <em>may</em> be structured with <code>_</code> separators, e.g. |
| <code>testX_whenY_doesZ()</code>.</p> |
| |
| <h4 id="identifiers-underscore-prefix-suffix" class="numbered"><code>_</code> prefix/suffix</h4> |
| |
| <p>Identifiers must not use <code>_</code> as a prefix or suffix.</p> |
| |
| <p>This also means that <code>_</code> <em>must not</em> be used as an identifier by itself (e.g. to |
| indicate a parameter is unused).</p> |
| |
| <blockquote> |
| <p>Tip: If you only need some of the elements from an array (or TypeScript |
| tuple), you can insert extra commas in a destructuring statement to ignore |
| in-between elements:</p> |
| |
| <pre><code class="language-ts">const [a, , b] = [1, 5, 10]; // a <- 1, b <- 10 |
| </code></pre> |
| </blockquote> |
| |
| |
| |
| <h4 id="identifiers-imports" class="numbered">Imports</h4> |
| |
| <p>Module namespace imports are <code>lowerCamelCase</code> while files are <code>snake_case</code>, |
| which means that imports correctly will not match in casing style, such as</p> |
| |
| <pre><code class="language-ts good">import * as fooBar from './foo_bar'; |
| </code></pre> |
| |
| <p>Some libraries might commonly use a namespace import prefix that violates this |
| naming scheme, but overbearingly common open source use makes the violating |
| style more readable. The only libraries that currently fall under this exception |
| are:</p> |
| |
| <ul> |
| <li><a href="https://jquery.com/">jquery</a>, using the <code>$</code> prefix</li> |
| <li><a href="https://threejs.org/">threejs</a>, using the <code>THREE</code> prefix</li> |
| </ul> |
| |
| <h4 id="identifiers-constants" class="numbered">Constants</h4> |
| |
| <p><strong>Immutable</strong>: <code>CONSTANT_CASE</code> indicates that a value is <em>intended</em> to not be |
| changed, and <em>may</em> 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.</p> |
| |
| <pre><code class="language-ts good">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. |
| </code></pre> |
| |
| <p>A constant can also be a <code>static readonly</code> property of a class.</p> |
| |
| <pre><code class="language-ts good">class Foo { |
| private static readonly MY_SPECIAL_NUMBER = 5; |
| |
| bar() { |
| return 2 * Foo.MY_SPECIAL_NUMBER; |
| } |
| } |
| </code></pre> |
| |
| <p><strong>Global</strong>: Only symbols declared on the module level, static fields of module |
| level classes, and values of module level enums, <em>may</em> use <code>CONST_CASE</code>. If a |
| value can be instantiated more than once over the lifetime of the program (e.g. |
| a local variable declared within a function, or a static field on a class nested |
| in a function) then it <em>must</em> use <code>lowerCamelCase</code>.</p> |
| |
| <p>If a value is an arrow function that implements an interface, then it <em>may</em> be |
| declared <code>lowerCamelCase</code>.</p> |
| |
| |
| |
| |
| |
| <p><a id="naming-local-aliases"></a></p> |
| |
| <h4 id="aliases" class="numbered">Aliases</h4> |
| |
| <p>When creating a local-scope alias of an existing symbol, use the format of the |
| existing identifier. The local alias <em>must</em> match the existing naming and format |
| of the source. For variables use <code>const</code> for your local aliases, and for class |
| fields use the <code>readonly</code> attribute.</p> |
| |
| <blockquote> |
| <p>Note: If you're creating an alias just to expose it to a template in your |
| framework of choice, remember to also apply the proper |
| <a href="#properties-used-outside-of-class-lexical-scope">access modifiers</a>.</p> |
| </blockquote> |
| |
| <pre><code class="language-ts good">const {BrewStateEnum} = SomeType; |
| const CAPACITY = 5; |
| |
| class Teapot { |
| readonly BrewStateEnum = BrewStateEnum; |
| readonly CAPACITY = CAPACITY; |
| } |
| </code></pre> |
| |
| <h2 id="type-system" class="numbered">Type system</h2> |
| |
| <h3 id="type-inference" class="numbered">Type inference</h3> |
| |
| <p>Code <em>may</em> rely on type inference as implemented by the TypeScript compiler for |
| all type expressions (variables, fields, return types, etc). |
| </p> |
| |
| <pre><code class="language-ts good">const x = 15; // Type inferred. |
| </code></pre> |
| |
| <p>Leave out type annotations for trivially inferred types: variables or parameters |
| initialized to a <code>string</code>, <code>number</code>, <code>boolean</code>, <code>RegExp</code> literal or <code>new</code> |
| expression.</p> |
| |
| <pre><code class="language-ts bad">const x: boolean = true; // Bad: 'boolean' here does not aid readability |
| </code></pre> |
| |
| <pre><code class="language-ts bad">// Bad: 'Set' is trivially inferred from the initialization |
| const x: Set<string> = new Set(); |
| </code></pre> |
| |
| <p>Explicitly specifying types may be required to prevent generic type parameters |
| from being inferred as <code>unknown</code>. For example, initializing generic types with |
| no values (e.g. empty arrays, objects, <code>Map</code>s, or <code>Set</code>s).</p> |
| |
| <pre><code class="language-ts good">const x = new Set<string>(); |
| </code></pre> |
| |
| <p>For more complex expressions, type annotations can help with readability of the |
| program:</p> |
| |
| <pre><code class="language-ts bad">// Hard to reason about the type of 'value' without an annotation. |
| const value = await rpc.getSomeValue().transform(); |
| </code></pre> |
| |
| <pre><code class="language-ts good">// Can tell the type of 'value' at a glance. |
| const value: string[] = await rpc.getSomeValue().transform(); |
| </code></pre> |
| |
| <p>Whether an annotation is required is decided by the code reviewer.</p> |
| |
| |
| |
| <h4 id="return-types" class="numbered">Return types</h4> |
| |
| <p>Whether to include return type annotations for functions and methods is up to |
| the code author. Reviewers <em>may</em> ask for annotations to clarify complex return |
| types that are hard to understand. Projects <em>may</em> have a local policy to always |
| require return types, but this is not a general TypeScript style requirement.</p> |
| |
| <p>There are two benefits to explicitly typing out the implicit return values of |
| functions and methods:</p> |
| |
| <ul> |
| <li>More precise documentation to benefit readers of the code.</li> |
| <li>Surface potential type errors faster in the future if there are code changes |
| that change the return type of the function.</li> |
| </ul> |
| |
| <p><a id="null-vs-undefined"></a></p> |
| |
| <h3 id="undefined-and-null" class="numbered">Undefined and null</h3> |
| |
| <p>TypeScript supports <code>undefined</code> and <code>null</code> types. Nullable types can be |
| constructed as a union type (<code>string|null</code>); similarly with <code>undefined</code>. There |
| is no special syntax for unions of <code>undefined</code> and <code>null</code>.</p> |
| |
| |
| |
| <p>TypeScript code can use either <code>undefined</code> or <code>null</code> to denote absence of a |
| value, there is no general guidance to prefer one over the other. Many |
| JavaScript APIs use <code>undefined</code> (e.g. <code>Map.get</code>), while many DOM and Google APIs |
| use <code>null</code> (e.g. <code>Element.getAttribute</code>), so the appropriate absent value |
| depends on the context.</p> |
| |
| <h4 id="nullableundefined-type-aliases" class="numbered">Nullable/undefined type aliases</h4> |
| |
| <p>Type aliases <em>must not</em> include <code>|null</code> or <code>|undefined</code> in a union type. |
| Nullable aliases typically indicate that null values are being passed around |
| through too many layers of an application, and this clouds the source of the |
| original issue that resulted in <code>null</code>. They also make it unclear when specific |
| values on a class or interface might be absent.</p> |
| |
| <p>Instead, code <em>must</em> only add <code>|null</code> or <code>|undefined</code> when the alias is actually |
| used. Code <em>should</em> deal with null values close to where they arise, using the |
| above techniques.</p> |
| |
| <pre><code class="language-ts bad">// Bad |
| type CoffeeResponse = Latte|Americano|undefined; |
| |
| class CoffeeService { |
| getLatte(): CoffeeResponse { ... }; |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts good">// Better |
| type CoffeeResponse = Latte|Americano; |
| |
| class CoffeeService { |
| getLatte(): CoffeeResponse|undefined { ... }; |
| } |
| </code></pre> |
| |
| <p><a id="optionals-vs-undefined-type"></a></p> |
| |
| <h4 id="prefer-optional-over-undefined" class="numbered">Prefer optional over <code>|undefined</code></h4> |
| |
| <p>In addition, TypeScript supports a special construct for optional parameters and |
| fields, using <code>?</code>:</p> |
| |
| <pre><code class="language-ts good">interface CoffeeOrder { |
| sugarCubes: number; |
| milk?: Whole|LowFat|HalfHalf; |
| } |
| |
| function pourCoffee(volume?: Milliliter) { ... } |
| </code></pre> |
| |
| <p>Optional parameters implicitly include <code>|undefined</code> in their type. However, they |
| are different in that they can be left out when constructing a value or calling |
| a method. For example, <code>{sugarCubes: 1}</code> is a valid <code>CoffeeOrder</code> because <code>milk</code> |
| is optional.</p> |
| |
| <p>Use optional fields (on interfaces or classes) and parameters rather than a |
| <code>|undefined</code> type.</p> |
| |
| <p>For classes preferably avoid this pattern altogether and initialize as many |
| fields as possible.</p> |
| |
| <pre><code class="language-ts good">class MyClass { |
| field = ''; |
| } |
| </code></pre> |
| |
| <p><a id="structural-types-vs-nominal-types"></a></p> |
| |
| <h3 id="use-structural-types" class="numbered">Use structural types</h3> |
| |
| <p>TypeScript's type system is structural, not nominal. That is, a value matches a |
| type if it has at least all the properties the type requires and the properties' |
| types match, recursively.</p> |
| |
| <p>When providing a structural-based implementation, explicitly include the type at |
| the declaration of the symbol (this allows more precise type checking and error |
| reporting).</p> |
| |
| <pre><code class="language-ts good">const foo: Foo = { |
| a: 123, |
| b: 'abc', |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">const badFoo = { |
| a: 123, |
| b: 'abc', |
| } |
| </code></pre> |
| |
| <p>Use interfaces to define structural types, not classes</p> |
| |
| <pre><code class="language-ts good">interface Foo { |
| a: number; |
| b: string; |
| } |
| |
| const foo: Foo = { |
| a: 123, |
| b: 'abc', |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">class Foo { |
| readonly a: number; |
| readonly b: number; |
| } |
| |
| const foo: Foo = { |
| a: 123, |
| b: 'abc', |
| } |
| </code></pre> |
| |
| <section class="zippy"> |
| |
| <p>Why?</p> |
| |
| <p>The <q>badFoo</q> object above relies on type inference. Additional fields could be |
| added to <q>badFoo</q> and the type is inferred based on the object itself.</p> |
| |
| <p>When passing a <q>badFoo</q> to a function that takes a <q>Foo</q>, the error will be at |
| the function call site, rather than at the object declaration site. This is also |
| useful when changing the surface of an interface across broad codebases.</p> |
| |
| <pre><code class="language-ts">interface Animal { |
| sound: string; |
| name: string; |
| } |
| |
| function makeSound(animal: Animal) {} |
| |
| /** |
| * 'cat' has an inferred type of '{sound: string}' |
| */ |
| const cat = { |
| sound: 'meow', |
| }; |
| |
| /** |
| * 'cat' does not meet the type contract required for the function, so the |
| * TypeScript compiler errors here, which may be very far from where 'cat' is |
| * defined. |
| */ |
| makeSound(cat); |
| |
| /** |
| * Horse has a structural type and the type error shows here rather than the |
| * function call. 'horse' does not meet the type contract of 'Animal'. |
| */ |
| const horse: Animal = { |
| sound: 'niegh', |
| }; |
| |
| const dog: Animal = { |
| sound: 'bark', |
| name: 'MrPickles', |
| }; |
| |
| makeSound(dog); |
| makeSound(horse); |
| </code></pre> |
| |
| </section> |
| |
| <p><a id="interfaces-vs-type-aliases"></a></p> |
| |
| <h3 id="prefer-interfaces" class="numbered">Prefer interfaces over type literal aliases</h3> |
| |
| <p>TypeScript supports |
| <a href="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-aliases">type aliases</a> |
| for naming a type expression. This can be used to name primitives, unions, |
| tuples, and any other types.</p> |
| |
| <p>However, when declaring types for objects, use interfaces instead of a type |
| alias for the object literal expression.</p> |
| |
| <pre><code class="language-ts good">interface User { |
| firstName: string; |
| lastName: string; |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">type User = { |
| firstName: string, |
| lastName: string, |
| } |
| </code></pre> |
| |
| <section class="zippy"> |
| |
| <p>Why?</p> |
| |
| <p>These forms are nearly equivalent, so under the principle of just choosing one |
| out of two forms to prevent variation, we should choose one. Additionally, there |
| are also |
| <a href="https://ncjamieson.com/prefer-interfaces/">interesting technical reasons to prefer interface</a>. |
| That page quotes the TypeScript team lead: <q>Honestly, my take is that it should |
| really just be interfaces for anything that they can model. There is no benefit |
| to type aliases when there are so many issues around display/perf.</q></p> |
| |
| </section> |
| |
| <h3 id="arrayt-type" class="numbered"><code>Array<T></code> Type</h3> |
| |
| <p>For simple types (containing just alphanumeric characters and dot), use the |
| syntax sugar for arrays, <code>T[]</code> or <code>readonly T[]</code>, rather than the longer form |
| <code>Array<T></code> or <code>ReadonlyArray<T></code>.</p> |
| |
| <p>For multi-dimensional non-<code>readonly</code> arrays of simple types, use the syntax |
| sugar form (<code>T[][]</code>, <code>T[][][]</code>, and so on) rather than the longer form.</p> |
| |
| <p>For anything more complex, use the longer form <code>Array<T></code>.</p> |
| |
| <p>These rules apply at each level of nesting, i.e. a simple <code>T[]</code> nested in a more |
| complex type would still be spelled as <code>T[]</code>, using the syntax sugar.</p> |
| |
| <pre><code class="language-ts good">let a: string[]; |
| let b: readonly string[]; |
| let c: ns.MyObj[]; |
| let d: string[][]; |
| let e: Array<{n: number, s: string}>; |
| let f: Array<string|number>; |
| let g: ReadonlyArray<string|number>; |
| let h: InjectionToken<string[]>; // Use syntax sugar for nested types. |
| let i: ReadonlyArray<string[]>; |
| let j: Array<readonly string[]>; |
| </code></pre> |
| |
| <pre><code class="language-ts bad">let a: Array<string>; // The syntax sugar is shorter. |
| let b: ReadonlyArray<string>; |
| let c: Array<ns.MyObj>; |
| let d: Array<string[]>; |
| let e: {n: number, s: string}[]; // The braces make it harder to read. |
| let f: (string|number)[]; // Likewise with parens. |
| let g: readonly (string | number)[]; |
| let h: InjectionToken<Array<string>>; |
| let i: readonly string[][]; |
| let j: (readonly string[])[]; |
| </code></pre> |
| |
| <h3 id="indexable-key-string-type" class="numbered">Indexable types / index signatures (<code>{[key: string]: T}</code>)</h3> |
| |
| <p>In JavaScript, it's common to use an object as an associative array (aka <q>map</q>, |
| <q>hash</q>, or <q>dict</q>). Such objects can be typed using an |
| <a href="https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures">index signature</a> |
| (<code>[k: string]: T</code>) in TypeScript:</p> |
| |
| <pre><code class="language-ts">const fileSizes: {[fileName: string]: number} = {}; |
| fileSizes['readme.txt'] = 541; |
| </code></pre> |
| |
| <p>In TypeScript, provide a meaningful label for the key. (The label only exists |
| for documentation; it's unused otherwise.)</p> |
| |
| <pre><code class="language-ts bad">const users: {[key: string]: number} = ...; |
| </code></pre> |
| |
| <pre><code class="language-ts good">const users: {[userName: string]: number} = ...; |
| </code></pre> |
| |
| <blockquote> |
| <p>Rather than using one of these, consider using the ES6 <code>Map</code> and <code>Set</code> types |
| instead. JavaScript objects have |
| <a href="http://2ality.com/2012/01/objects-as-maps.html">surprising undesirable behaviors</a> |
| and the ES6 types more explicitly convey your intent. Also, <code>Map</code>s can be |
| keyed by—and <code>Set</code>s can contain—types other than <code>string</code>.</p> |
| </blockquote> |
| |
| <p>TypeScript's builtin <code>Record<Keys, ValueType></code> type allows constructing types |
| with a defined set of keys. This is distinct from associative arrays in that the |
| keys are statically known. See advice on that |
| <a href="#mapped-conditional-types">below</a>.</p> |
| |
| <h3 id="mapped-conditional-types" class="numbered">Mapped and conditional types</h3> |
| |
| <p>TypeScript's |
| <a href="https://www.typescriptlang.org/docs/handbook/2/mapped-types.html">mapped types</a> |
| and |
| <a href="https://www.typescriptlang.org/docs/handbook/2/conditional-types.html">conditional types</a> |
| allow specifying new types based on other types. TypeScript's standard library |
| includes several type operators based on these (<code>Record</code>, <code>Partial</code>, <code>Readonly</code> |
| etc).</p> |
| |
| <p>These type system features allow succinctly specifying types and constructing |
| powerful yet type safe abstractions. They come with a number of drawbacks |
| though:</p> |
| |
| <ul> |
| <li>Compared to explicitly specifying properties and type relations (e.g. using |
| interfaces and extension, see below for an example), type operators require |
| the reader to mentally evaluate the type expression. This can make programs |
| substantially harder to read, in particular combined with type inference and |
| expressions crossing file boundaries.</li> |
| <li>Mapped & conditional types' evaluation model, in particular when combined |
| with type inference, is underspecified, not always well understood, and |
| often subject to change in TypeScript compiler versions. Code can |
| <q>accidentally</q> compile or seem to give the right results. This increases |
| future support cost of code using type operators.</li> |
| <li>Mapped & conditional types are most powerful when deriving types from |
| complex and/or inferred types. On the flip side, this is also when they are |
| most prone to create hard to understand and maintain programs.</li> |
| <li>Some language tooling does not work well with these type system features. |
| E.g. your IDE's find references (and thus rename property refactoring) will |
| not find properties in a <code>Pick<T, Keys></code> type, and Code Search won't |
| hyperlink them.</li> |
| <li> |
| </ul> |
| |
| <p>The style recommendation is:</p> |
| |
| <ul> |
| <li>Always use the simplest type construct that can possibly express your code.</li> |
| <li>A little bit of repetition or verbosity is often much cheaper than the long |
| term cost of complex type expressions.</li> |
| <li>Mapped & conditional types may be used, subject to these considerations.</li> |
| </ul> |
| |
| <p>For example, TypeScript's builtin <code>Pick<T, Keys></code> type allows creating a new |
| type by subsetting another type <code>T</code>, but simple interface extension can often be |
| easier to understand.</p> |
| |
| <pre><code class="language-ts">interface User { |
| shoeSize: number; |
| favoriteIcecream: string; |
| favoriteChocolate: string; |
| } |
| |
| // FoodPreferences has favoriteIcecream and favoriteChocolate, but not shoeSize. |
| type FoodPreferences = Pick<User, 'favoriteIcecream'|'favoriteChocolate'>; |
| </code></pre> |
| |
| <p>This is equivalent to spelling out the properties on <code>FoodPreferences</code>:</p> |
| |
| <pre><code class="language-ts">interface FoodPreferences { |
| favoriteIcecream: string; |
| favoriteChocolate: string; |
| } |
| </code></pre> |
| |
| <p>To reduce duplication, <code>User</code> could extend <code>FoodPreferences</code>, or (possibly |
| better) nest a field for food preferences:</p> |
| |
| <pre><code class="language-ts good">interface FoodPreferences { /* as above */ } |
| interface User extends FoodPreferences { |
| shoeSize: number; |
| // also includes the preferences. |
| } |
| </code></pre> |
| |
| <p>Using interfaces here makes the grouping of properties explicit, improves IDE |
| support, allows better optimization, and arguably makes the code easier to |
| understand.</p> |
| |
| <h3 id="any" class="numbered"><code>any</code> Type</h3> |
| |
| <p>TypeScript's <code>any</code> type is a super and subtype of all other types, and allows |
| dereferencing all properties. As such, <code>any</code> is dangerous - it can mask severe |
| programming errors, and its use undermines the value of having static types in |
| the first place.</p> |
| |
| |
| |
| <section> |
| |
| <p><strong>Consider <em>not</em> to use <code>any</code>.</strong> In circumstances where you want to use <code>any</code>, |
| consider one of:</p> |
| |
| </section> |
| |
| <ul> |
| <li><a href="#any-specific">Provide a more specific type</a></li> |
| <li><a href="#any-unknown">Use <code>unknown</code></a></li> |
| <li><a href="#any-suppress">Suppress the lint warning and document why</a></li> |
| </ul> |
| |
| <h4 id="any-specific" class="numbered">Providing a more specific type</h4> |
| |
| <p>Use interfaces , an |
| inline object type, or a type alias:</p> |
| |
| <pre><code class="language-ts good">// Use declared interfaces to represent server-side JSON. |
| declare interface MyUserJson { |
| name: string; |
| email: string; |
| } |
| |
| // Use type aliases for types that are repetitive to write. |
| type MyType = number|string; |
| |
| // Or use inline object types for complex returns. |
| function getTwoThings(): {something: number, other: string} { |
| // ... |
| return {something, other}; |
| } |
| |
| // Use a generic type, where otherwise a library would say `any` to represent |
| // they don't care what type the user is operating on (but note "Return type |
| // only generics" below). |
| function nicestElement<T>(items: T[]): T { |
| // Find the nicest element in items. |
| // Code can also put constraints on T, e.g. <T extends HTMLElement>. |
| } |
| </code></pre> |
| |
| <h4 id="any-unknown" class="numbered">Using <code>unknown</code> over <code>any</code></h4> |
| |
| <p>The <code>any</code> type allows assignment into any other type and dereferencing any |
| property off it. Often this behaviour is not necessary or desirable, and code |
| just needs to express that a type is unknown. Use the built-in type <code>unknown</code> in |
| that situation — it expresses the concept and is much safer as it does not allow |
| dereferencing arbitrary properties.</p> |
| |
| <pre><code class="language-ts good">// Can assign any value (including null or undefined) into this but cannot |
| // use it without narrowing the type or casting. |
| const val: unknown = value; |
| </code></pre> |
| |
| <pre><code class="language-ts bad">const danger: any = value /* result of an arbitrary expression */; |
| danger.whoops(); // This access is completely unchecked! |
| </code></pre> |
| |
| <section> |
| |
| <p>To safely use <code>unknown</code> values, narrow the type using a |
| <a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types">type guard</a></p> |
| |
| </section> |
| |
| |
| |
| <h4 id="any-suppress" class="numbered">Suppressing <code>any</code> lint warnings</h4> |
| |
| <p>Sometimes using <code>any</code> is legitimate, for example in tests to construct a mock |
| object. In such cases, add a comment that suppresses the lint warning, and |
| document why it is legitimate.</p> |
| |
| <pre><code class="language-ts good">// This test only needs a partial implementation of BookService, and if |
| // we overlooked something the test will fail in an obvious way. |
| // This is an intentionally unsafe partial mock |
| // tslint:disable-next-line:no-any |
| const mockBookService = ({get() { return mockBook; }} as any) as BookService; |
| // Shopping cart is not used in this test |
| // tslint:disable-next-line:no-any |
| const component = new MyComponent(mockBookService, /* unused ShoppingCart */ null as any); |
| </code></pre> |
| |
| |
| |
| <h3 id="empty-interface-type" class="numbered"><code>{}</code> Type</h3> |
| |
| <p>The <code>{}</code> type, also known as an <em>empty interface</em> type, represents a interface |
| with no properties. An empty interface type has no specified properties and |
| therefore any non-nullish value is assignable to it.</p> |
| |
| <pre><code class="language-ts bad">let player: {}; |
| |
| player = { |
| health: 50, |
| }; // Allowed. |
| |
| console.log(player.health) // Property 'health' does not exist on type '{}'. |
| </code></pre> |
| |
| <pre><code class="language-ts bad">function takeAnything(obj:{}) { |
| |
| } |
| |
| takeAnything({}); |
| takeAnything({ a: 1, b: 2 }); |
| </code></pre> |
| |
| <p>Google3 code <strong>should not</strong> use <code>{}</code> for most use cases. <code>{}</code> represents any |
| non-nullish primitive or object type, which is rarely appropriate. Prefer one of |
| the following more-descriptive types:</p> |
| |
| <ul> |
| <li><code>unknown</code> can hold any value, including <code>null</code> or <code>undefined</code>, and is |
| generally more appropriate for opaque values.</li> |
| <li><code>Record<string, T></code> is better for dictionary-like objects, and provides |
| better type safety by being explicit about the type <code>T</code> of contained values |
| (which may itself be <code>unknown</code>).</li> |
| <li><code>object</code> excludes primitives as well, leaving only non-nullish functions and |
| objects, but without any other assumptions about what properties may be |
| available.</li> |
| </ul> |
| |
| <h3 id="tuple-types" class="numbered">Tuple types</h3> |
| |
| <p>If you are tempted to create a Pair type, instead use a tuple type:</p> |
| |
| <pre><code class="language-ts bad">interface Pair { |
| first: string; |
| second: string; |
| } |
| function splitInHalf(input: string): Pair { |
| ... |
| return {first: x, second: y}; |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts good">function splitInHalf(input: string): [string, string] { |
| ... |
| return [x, y]; |
| } |
| |
| // Use it like: |
| const [leftHalf, rightHalf] = splitInHalf('my string'); |
| </code></pre> |
| |
| <p>However, often it's clearer to provide meaningful names for the properties.</p> |
| |
| <p>If declaring an <code>interface</code> is too heavyweight, you can use an inline object |
| literal type:</p> |
| |
| <pre><code class="language-ts good">function splitHostPort(address: string): {host: string, port: number} { |
| ... |
| } |
| |
| // Use it like: |
| const address = splitHostPort(userAddress); |
| use(address.port); |
| |
| // You can also get tuple-like behavior using destructuring: |
| const {host, port} = splitHostPort(userAddress); |
| </code></pre> |
| |
| <h3 id="wrapper-types" class="numbered">Wrapper types</h3> |
| |
| <p>There are a few types related to JavaScript primitives that <em>should not</em> ever be |
| used:</p> |
| |
| <ul> |
| <li><code>String</code>, <code>Boolean</code>, and <code>Number</code> have slightly different meaning from the |
| corresponding primitive types <code>string</code>, <code>boolean</code>, and <code>number</code>. Always use |
| the lowercase version.</li> |
| <li><code>Object</code> has similarities to both <code>{}</code> and <code>object</code>, but is slightly looser. |
| Use <code>{}</code> for a type that include everything except <code>null</code> and <code>undefined</code>, |
| or lowercase <code>object</code> to further exclude the other primitive types (the |
| three mentioned above, plus <code>symbol</code> and <code>bigint</code>).</li> |
| </ul> |
| |
| <p>Further, never invoke the wrapper types as constructors (with <code>new</code>).</p> |
| |
| <h3 id="return-type-only-generics" class="numbered">Return type only generics</h3> |
| |
| <p>Avoid creating APIs that have return type only generics. When working with |
| existing APIs that have return type only generics always explicitly specify the |
| generics.</p> |
| |
| |
| |
| <p><a id="appendices-style-related-tools"></a></p> |
| |
| <h2 id="toolchain-requirements" class="numbered">Toolchain requirements</h2> |
| |
| <p>Google style requires using a number of tools in specific ways, outlined here.</p> |
| |
| <p><a id="clang-format"></a> |
| <a id="google-format"></a> |
| <a id="use-the-google3-formatter"></a> |
| <a id="source-code-formatting"></a> |
| <a id="appendices-clang-format"></a> |
| <a id="formatting"></a> |
| <a id="formatting-array-literals"></a> |
| <a id="formatting-block-indentation"></a> |
| <a id="formatting-column-limit"></a> |
| <a id="formatting-empty-blocks"></a> |
| <a id="formatting-function-expressions"></a> |
| <a id="formatting-nonempty-blocks"></a> |
| <a id="formatting-object-literals"></a> |
| <a id="formatting-one-statement-perline"></a> |
| <a id="formatting-statements"></a> |
| <a id="formatting-switch-statements"></a> |
| <a id="formatting-vertical-whitespace"></a> |
| <a id="formatting-whitespace"></a></p> |
| |
| |
| |
| <h3 id="typescript-compiler" class="numbered">TypeScript compiler</h3> |
| |
| <p>All TypeScript files must pass type checking using the standard |
| tool chain.</p> |
| |
| <h4 id="ts-ignore" class="numbered">@ts-ignore</h4> |
| |
| <p>Do not use <code>@ts-ignore</code> nor the variants <code>@ts-expect-error</code> or <code>@ts-nocheck</code>.</p> |
| |
| <section class="zippy"> |
| |
| <p>Why?</p> |
| |
| <p>They superficially seem to be an easy way to <q>fix</q> a compiler error, but in |
| practice, a specific compiler error is often caused by a larger problem that can |
| be fixed more directly.</p> |
| |
| <p>For example, if you are using <code>@ts-ignore</code> to suppress a type error, then it's |
| hard to predict what types the surrounding code will end up seeing. For many |
| type errors, the advice in <a href="#any">how to best use <code>any</code></a> is useful.</p> |
| |
| </section> |
| |
| |
| |
| <p>You may use <code>@ts-expect-error</code> in unit tests, though you generally <em>should not</em>. |
| <code>@ts-expect-error</code> suppresses all errors. It's easy to accidentally over-match |
| and suppress more serious errors. Consider one of:</p> |
| |
| <ul> |
| <li>When testing APIs that need to deal with unchecked values at runtime, add |
| casts to the expected type or to <code>any</code> and add an explanatory comment. This |
| limits error suppression to a single expression.</li> |
| <li>Suppress the lint warning and document why, similar to |
| <a href="#any-suppress">suppressing <code>any</code> lint warnings</a>.</li> |
| </ul> |
| |
| |
| |
| |
| |
| <h3 id="conformance" class="numbered">Conformance</h3> |
| |
| <p>Google TypeScript includes several <em>conformance frameworks</em>, |
| |
| <a href="https://tsetse.info">tsetse</a> and |
| <a href="https://github.com/google/tsec">tsec</a>.</p> |
| |
| |
| |
| <p>These rules are commonly used to enforce critical restrictions (such as defining |
| globals, which could break the codebase) and security patterns (such as using |
| <code>eval</code> or assigning to <code>innerHTML</code>), or more loosely to improve code quality.</p> |
| |
| <p>Google-style TypeScript must abide by any applicable global or framework-local |
| conformance rules.</p> |
| |
| |
| |
| |
| |
| <p><a id="jsdoc"></a> |
| <a id="formatting-comments"></a></p> |
| |
| <h2 id="comments-documentation" class="numbered">Comments and documentation</h2> |
| |
| |
| |
| <h4 id="jsdoc-vs-comments" class="numbered">JSDoc versus comments</h4> |
| |
| <p>There are two types of comments, JSDoc (<code>/** ... */</code>) and non-JSDoc ordinary |
| comments (<code>// ...</code> or <code>/* ... */</code>).</p> |
| |
| <ul> |
| <li>Use <code>/** JSDoc */</code> comments for documentation, i.e. comments a user of the |
| code should read.</li> |
| <li>Use <code>// line comments</code> for implementation comments, i.e. comments that only |
| concern the implementation of the code itself.</li> |
| </ul> |
| |
| <p>JSDoc comments are understood by tools (such as editors and documentation |
| generators), while ordinary comments are only for other humans.</p> |
| |
| <p><a id="formatting-block-comment-style"></a></p> |
| |
| <h4 id="multi-line-comments" class="numbered">Multi-line comments</h4> |
| |
| <p>Multi-line comments are indented at the same level as the surrounding code. They |
| <em>must</em> use multiple single-line comments (<code>//</code>-style), not block comment style |
| (<code>/* */</code>).</p> |
| |
| <pre><code class="language-ts good">// This is |
| // fine |
| </code></pre> |
| |
| <pre><code class="language-ts bad">/* |
| * This should |
| * use multiple |
| * single-line comments |
| */ |
| |
| /* This should use // */ |
| </code></pre> |
| |
| <p>Comments are not enclosed in boxes drawn with asterisks or other characters.</p> |
| |
| |
| |
| <h3 id="jsdoc-general-form" class="numbered">JSDoc general form</h3> |
| |
| <p>The basic formatting of JSDoc comments is as seen in this example:</p> |
| |
| <pre><code class="language-ts good">/** |
| * Multiple lines of JSDoc text are written here, |
| * wrapped normally. |
| * @param arg A number to do something to. |
| */ |
| function doSomething(arg: number) { … } |
| </code></pre> |
| |
| <p>or in this single-line example:</p> |
| |
| <pre><code class="language-ts good">/** This short jsdoc describes the function. */ |
| function doSomething(arg: number) { … } |
| </code></pre> |
| |
| <p>If a single-line comment overflows into multiple lines, it <em>must</em> use the |
| multi-line style with <code>/**</code> and <code>*/</code> on their own lines.</p> |
| |
| <p>Many tools extract metadata from JSDoc comments to perform code validation and |
| optimization. As such, these comments <em>must</em> be well-formed.</p> |
| |
| <h3 id="jsdoc-markdown" class="numbered">Markdown</h3> |
| |
| <p>JSDoc is written in Markdown, though it <em>may</em> include HTML when necessary.</p> |
| |
| <p>This means that tooling parsing JSDoc will ignore plain text formatting, so if |
| you did this:</p> |
| |
| <pre><code class="language-ts bad">/** |
| * Computes weight based on three factors: |
| * items sent |
| * items received |
| * last timestamp |
| */ |
| </code></pre> |
| |
| <p>it will be rendered like this:</p> |
| |
| <pre><code>Computes weight based on three factors: items sent items received last timestamp |
| </code></pre> |
| |
| <p>Instead, write a Markdown list:</p> |
| |
| <pre><code class="language-ts good">/** |
| * Computes weight based on three factors: |
| * |
| * - items sent |
| * - items received |
| * - last timestamp |
| */ |
| </code></pre> |
| |
| <h3 id="jsdoc-tags" class="numbered">JSDoc tags</h3> |
| |
| <p>Google style allows a subset of JSDoc tags. Most tags must occupy their own line, with the tag at the beginning |
| of the line.</p> |
| |
| <pre><code class="language-ts good">/** |
| * The "param" tag must occupy its own line and may not be combined. |
| * @param left A description of the left param. |
| * @param right A description of the right param. |
| */ |
| function add(left: number, right: number) { ... } |
| </code></pre> |
| |
| <pre><code class="language-ts bad">/** |
| * The "param" tag must occupy its own line and may not be combined. |
| * @param left @param right |
| */ |
| function add(left: number, right: number) { ... } |
| </code></pre> |
| |
| |
| |
| <h3 id="jsdoc-line-wrapping" class="numbered">Line wrapping</h3> |
| |
| <p>Line-wrapped block tags are indented four spaces. Wrapped description text <em>may</em> |
| be lined up with the description on previous lines, but this horizontal |
| alignment is discouraged.</p> |
| |
| <pre><code class="language-ts good">/** |
| * Illustrates line wrapping for long param/return descriptions. |
| * @param foo This is a param with a particularly long description that just |
| * doesn't fit on one line. |
| * @return This returns something that has a lengthy description too long to fit |
| * in one line. |
| */ |
| exports.method = function(foo) { |
| return 5; |
| }; |
| </code></pre> |
| |
| <p>Do not indent when wrapping a <code>@desc</code> or <code>@fileoverview</code> description.</p> |
| |
| <h3 id="document-all-top-level-exports-of-modules" class="numbered">Document all top-level exports of modules</h3> |
| |
| <p>Use <code>/** JSDoc */</code> comments to communicate information to the users of your |
| code. Avoid merely restating the property or parameter name. You <em>should</em> also |
| document all properties and methods (exported/public or not) whose purpose is |
| not immediately obvious from their name, as judged by your reviewer.</p> |
| |
| <p><strong>Exception:</strong> Symbols that are only exported to be consumed by tooling, such as |
| @NgModule classes, do not require comments.</p> |
| |
| <h3 id="jsdoc-class-comments" class="numbered">Class comments</h3> |
| |
| <p>JSDoc comments for classes should provide the reader with enough information to |
| know how and when to use the class, as well as any additional considerations |
| necessary to correctly use the class. Textual descriptions may be omitted on the |
| constructor.</p> |
| |
| |
| |
| <h3 id="method-and-function-comments" class="numbered">Method and function comments</h3> |
| |
| <p>Method, parameter, and return descriptions may be omitted if they are obvious |
| from the rest of the method’s JSDoc or from the method name and type signature.</p> |
| |
| <p>Method descriptions begin with a verb phrase that describes what the method |
| does. This phrase is not an imperative sentence, but instead is written in the |
| third person, as if there is an implied <q>This method ...</q> before it.</p> |
| |
| <h3 id="parameter-property-comments" class="numbered">Parameter property comments</h3> |
| |
| <p>A |
| <a href="https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties">parameter property</a> |
| is a constructor parameter that is prefixed by one of the modifiers <code>private</code>, |
| <code>protected</code>, <code>public</code>, or <code>readonly</code>. A parameter property declares both a |
| parameter and an instance property, and implicitly assigns into it. For example, |
| <code>constructor(private readonly foo: Foo)</code>, declares that the constructor takes a |
| parameter <code>foo</code>, but also declares a private readonly property <code>foo</code>, and |
| assigns the parameter into that property before executing the remainder of the |
| constructor.</p> |
| |
| <p>To document these fields, use JSDoc's <code>@param</code> annotation. Editors display the |
| description on constructor calls and property accesses.</p> |
| |
| |
| |
| <pre><code class="language-ts good">/** This class demonstrates how parameter properties are documented. */ |
| class ParamProps { |
| /** |
| * @param percolator The percolator used for brewing. |
| * @param beans The beans to brew. |
| */ |
| constructor( |
| private readonly percolator: Percolator, |
| private readonly beans: CoffeeBean[]) {} |
| } |
| </code></pre> |
| |
| <pre><code class="language-ts good">/** This class demonstrates how ordinary fields are documented. */ |
| class OrdinaryClass { |
| /** The bean that will be used in the next call to brew(). */ |
| nextBean: CoffeeBean; |
| |
| constructor(initialBean: CoffeeBean) { |
| this.nextBean = initialBean; |
| } |
| } |
| </code></pre> |
| |
| <p><a id="omit-comments-that-are-redundant-with-typescript"></a> |
| <a id="do-not-use-override"></a></p> |
| |
| <h3 id="jsdoc-type-annotations" class="numbered">JSDoc type annotations</h3> |
| |
| <p>JSDoc type annotations are redundant in TypeScript source code. Do not declare |
| types in <code>@param</code> or <code>@return</code> blocks, do not write <code>@implements</code>, <code>@enum</code>, |
| <code>@private</code>, <code>@override</code> etc. on code that uses the <code>implements</code>, <code>enum</code>, |
| <code>private</code>, <code>override</code> etc. keywords.</p> |
| |
| |
| |
| <h3 id="redundant-comments" class="numbered">Make comments that actually add information</h3> |
| |
| <p>For non-exported symbols, sometimes the name and type of the function or |
| parameter is enough. Code will <em>usually</em> benefit from more documentation than |
| just variable names though!</p> |
| |
| <ul> |
| <li><p>Avoid comments that just restate the parameter name and type, e.g.</p> |
| |
| <pre><code class="language-ts bad">/** @param fooBarService The Bar service for the Foo application. */ |
| </code></pre></li> |
| <li><p>Because of this rule, <code>@param</code> and <code>@return</code> lines are only required when |
| they add information, and <em>may</em> otherwise be omitted.</p> |
| |
| <pre><code class="language-ts good">/** |
| * POSTs the request to start coffee brewing. |
| * @param amountLitres The amount to brew. Must fit the pot size! |
| */ |
| brew(amountLitres: number, logger: Logger) { |
| // ... |
| } |
| </code></pre></li> |
| </ul> |
| |
| <p><a id="formatting-param-name-comments"></a></p> |
| |
| <h4 id="comments-when-calling-a-function" class="numbered">Comments when calling a function</h4> |
| |
| <p>“Parameter name” comments should be used whenever the method name and parameter |
| value do not sufficiently convey the meaning of the parameter.</p> |
| |
| <p>Before adding these comments, consider refactoring the method to instead accept |
| an interface and destructure it to greatly improve call-site |
| readability.</p> |
| |
| <p><q>Parameter name</q> comments go before the parameter value, and include the |
| parameter name and a <code>=</code> suffix:</p> |
| |
| <pre><code class="language-ts good">someFunction(obviousParam, /* shouldRender= */ true, /* name= */ 'hello'); |
| </code></pre> |
| |
| <p>Existing code may use a legacy parameter name comment style, which places these |
| comments ~after~ the parameter value and omits the <code>=</code>. Continuing to use this |
| style within the file for consistency is acceptable.</p> |
| |
| <pre><code class="language-ts">someFunction(obviousParam, true /* shouldRender */, 'hello' /* name */); |
| </code></pre> |
| |
| <h3 id="place-documentation-prior-to-decorators" class="numbered">Place documentation prior to decorators</h3> |
| |
| <p>When a class, method, or property have both decorators like <code>@Component</code> and |
| JsDoc, please make sure to write the JsDoc before the decorator.</p> |
| |
| <ul> |
| <li><p>Do not write JsDoc between the Decorator and the decorated statement.</p> |
| |
| <pre><code class="language-ts bad">@Component({ |
| selector: 'foo', |
| template: 'bar', |
| }) |
| /** Component that prints "bar". */ |
| export class FooComponent {} |
| </code></pre></li> |
| <li><p>Write the JsDoc block before the Decorator.</p> |
| |
| <pre><code class="language-ts good">/** Component that prints "bar". */ |
| @Component({ |
| selector: 'foo', |
| template: 'bar', |
| }) |
| export class FooComponent {} |
| </code></pre></li> |
| </ul> |
| |
| |
| |
| <h2 id="policies" class="numbered">Policies</h2> |
| |
| <p><a id="policies-be-consistent"></a> |
| <a id="policies-newly-added-code-use-google-style"></a></p> |
| |
| <h3 id="consistency" class="numbered">Consistency</h3> |
| |
| <p>For any style question that isn't settled definitively by this specification, do |
| what the other code in the same file is already doing (<q>be consistent</q>). If that |
| doesn't resolve the question, consider emulating the other files in the same |
| directory.</p> |
| |
| <p>Brand new files <em>must</em> use Google Style, regardless of the style choices of |
| other files in the same package. When adding new code to a file that is not in |
| Google Style, reformatting the existing code first is recommended, subject to |
| the advice <a href="#reformatting-existing-code">below</a>. If this reformatting is not |
| done, then new code <em>should</em> be as consistent as possible with existing code in |
| the same file, but <em>must not</em> violate the style guide.</p> |
| |
| <p><a id="policies-code-not-in-google-style"></a> |
| <a id="policies-reformatting-existing-code"></a></p> |
| |
| <h4 id="reformatting-existing-code" class="numbered">Reformatting existing code</h4> |
| |
| <p>You will occasionally encounter files in the codebase that are not in proper |
| Google Style. These may have come from an acquisition, or may have been written |
| before Google Style took a position on some issue, or may be in non-Google Style |
| for any other reason.</p> |
| |
| <p>When updating the style of existing code, follow these guidelines.</p> |
| |
| <ol> |
| <li>It is not required to change all existing code to meet current style |
| guidelines. Reformatting existing code is a trade-off between code churn and |
| consistency. Style rules evolve over time and these kinds of tweaks to |
| maintain compliance would create unnecessary churn. However, if significant |
| changes are being made to a file it is expected that the file will be in |
| Google Style.</li> |
| <li>Be careful not to allow opportunistic style fixes to muddle the focus of a |
| CL. If you find yourself making a lot of style changes that aren’t critical |
| to the central focus of a CL, promote those changes to a separate CL.</li> |
| </ol> |
| |
| |
| |
| |
| |
| <p><a id="policies-deprecation"></a></p> |
| |
| <h3 id="deprecation" class="numbered">Deprecation</h3> |
| |
| <p>Mark deprecated methods, classes or interfaces with an <code>@deprecated</code> JSDoc |
| annotation. A deprecation comment must include simple, clear directions for |
| people to fix their call sites.</p> |
| |
| |
| |
| <p><a id="policies-generated-code-mostly-exempt"></a></p> |
| |
| <h3 id="generated-code" class="numbered">Generated code: mostly exempt</h3> |
| |
| <p>Source code generated by the build process is not required to be in Google |
| Style. However, any generated identifiers that will be referenced from |
| hand-written source code must follow the naming requirements. As a special |
| exception, such identifiers are allowed to contain underscores, which may help |
| to avoid conflicts with hand-written identifiers.</p> |
| |
| |
| |
| <p><a id="goals"></a></p> |
| |
| <h4 id="style-guide-goals" class="numbered">Style guide goals</h4> |
| |
| <p>In general, engineers usually know best about what's needed in their code, so if |
| there are multiple options and the choice is situation dependent, we should let |
| decisions be made locally. So the default answer should be <q>leave it out</q>.</p> |
| |
| <p>The following points are the exceptions, which are the reasons we have some |
| global rules. Evaluate your style guide proposal against the following:</p> |
| |
| <ol> |
| <li><p><strong>Code should avoid patterns that are known to cause problems, especially |
| for users new to the language.</strong></p> |
| |
| </li> |
| <li><p><strong>Code across |
| projects should be consistent across |
| irrelevant variations.</strong></p> |
| |
| <p>When there are two options that are equivalent in a superficial way, we |
| should consider choosing one just so we don't divergently evolve for no |
| reason and avoid pointless debates in code reviews.</p> |
| |
| <p>Examples:</p> |
| |
| <ul> |
| <li>The capitalization style of names.</li> |
| <li><code>x as T</code> syntax vs the equivalent <code><T>x</code> syntax (disallowed).</li> |
| <li><code>Array<[number, number]></code> vs <code>[number, number][]</code>.</li> |
| </ul></li> |
| <li><p><strong>Code should be maintainable in the long term.</strong></p> |
| |
| <p>Code usually lives longer than the original author works on it, and the |
| TypeScript team must keep all of Google working into the future.</p> |
| |
| <p>Examples:</p> |
| |
| <ul> |
| <li>We use software to automate changes to code, so code is autoformatted so |
| it's easy for software to meet whitespace rules.</li> |
| <li>We require a single set of compiler flags, so a given TS library can be |
| written assuming a specific set of flags, and users can always safely |
| use a shared library.</li> |
| <li>Code must import the libraries it uses (<q>strict deps</q>) so that a |
| refactor in a dependency doesn't change the dependencies of its users.</li> |
| <li>We ask users to write tests. Without tests we cannot have confidence |
| that changes that we make to the language, don't break users.</li> |
| </ul></li> |
| <li><p><strong>Code reviewers should be focused on improving the quality of the code, not |
| enforcing arbitrary rules.</strong></p> |
| |
| <p>If it's possible to implement your rule as an |
| |
| automated check that is often a good sign. |
| This also supports principle 3.</p> |
| |
| <p>If it really just doesn't matter that much -- if it's an obscure corner of |
| the language or if it avoids a bug that is unlikely to occur -- it's |
| probably worth leaving out.</p></li> |
| </ol> |
| |
| |
| |
| <p><a id="appendices-legacy-exceptions"></a> |
| <a id="appendices-legacy-exceptions-declareLegacyNamespace"></a> |
| <a id="appendices-legacy-exceptions-forward-declare"></a> |
| <a id="appendices-legacy-exceptions-function"></a> |
| <a id="appendices-legacy-exceptions-goog-provide"></a> |
| <a id="appendices-legacy-exceptions-goog-provide-summary"></a> |
| <a id="appendices-legacy-exceptions-goog-scope"></a> |
| <a id="appendices-legacy-exceptions-module-get"></a> |
| <a id="appendices-legacy-exceptions-overview"></a> |
| <a id="appendices-legacy-exceptions-var"></a> |
| <a id="appendices-legacy-exceptions-var-const"></a> |
| <a id="appendices-legacy-exceptions-var-declare"></a> |
| <a id="appendices-legacy-exceptions-var-scope"></a> |
| <a id="features-classes-old-style"></a></p> |
| |
| <div class="footnotes"> |
| <hr> |
| <ol> |
| |
| <li id="fn1"> |
| <p>Namespace imports are often called 'module imports' <a href="#fnref1" rev="footnote">↩</a></p> |
| </li> |
| |
| <li id="fn2"> |
| <p>named imports are sometimes called 'destructuring |
| imports' because they use similar syntax to |
| destructuring assignments. <a href="#fnref2" rev="footnote">↩</a></p> |
| </li> |
| |
| </ol> |
| </div> |
| </div> |
| </body> |
| </html> |