├── .gitignore
├── README.md
├── linters
├── README.md
└── tslint.json
├── package.json
└── packages
└── eslint-config-airbnb
├── index.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Excel Micro TypeScript Style Guide
2 |
3 | *A mostly reasonable approach to TypeScript based off of [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)*
4 |
5 | ## Table of Contents
6 |
7 | 1. [Types](#types)
8 | 1. [References](#references)
9 | 1. [Objects](#objects)
10 | 1. [Arrays](#arrays)
11 | 1. [Destructuring](#destructuring)
12 | 1. [Strings](#strings)
13 | 1. [Functions](#functions)
14 | 1. [Arrow Functions](#arrow-functions)
15 | 1. [Constructors](#constructors)
16 | 1. [Modules](#modules)
17 | 1. [Iterators and Generators](#iterators-and-generators)
18 | 1. [Properties](#properties)
19 | 1. [Variables](#variables)
20 | 1. [Hoisting](#hoisting)
21 | 1. [Comparison Operators & Equality](#comparison-operators--equality)
22 | 1. [Blocks](#blocks)
23 | 1. [Comments](#comments)
24 | 1. [Whitespace](#whitespace)
25 | 1. [Commas](#commas)
26 | 1. [Semicolons](#semicolons)
27 | 1. [Type Casting & Coercion](#type-casting--coercion)
28 | 1. [Naming Conventions](#naming-conventions)
29 | 1. [Accessors](#accessors)
30 | 1. [Events](#events)
31 | 1. [jQuery](#jquery)
32 | 1. [Type Annotations](#type-annotations)
33 | 1. [Interfaces](#interfaces)
34 | 1. [Organization](#organization)
35 | 1. [ECMAScript 5 Compatibility](#ecmascript-5-compatibility)
36 | 1. [ECMAScript 6 Styles](#ecmascript-6-styles)
37 | 1. [Typescript 1.5 Styles](#typescript-1.5-styles)
38 | 1. [License](#license)
39 |
40 | ## Types
41 |
42 | - [1.1](#1.1) **Primitives**: When you access a primitive type you work directly on its value.
43 |
44 | + `string`
45 | + `number`
46 | + `boolean`
47 | + `null`
48 | + `undefined`
49 |
50 | ```javascript
51 | const foo = 1;
52 | let bar = foo;
53 |
54 | bar = 9;
55 |
56 | console.log(foo, bar); // => 1, 9
57 | ```
58 | - [1.2](#1.2) **Complex**: When you access a complex type you work on a reference to its value.
59 |
60 | + `object`
61 | + `array`
62 | + `function`
63 |
64 | ```javascript
65 | const foo = [1, 2];
66 | const bar = foo;
67 |
68 | bar[0] = 9;
69 |
70 | console.log(foo[0], bar[0]); // => 9, 9
71 | ```
72 |
73 | **[⬆ back to top](#table-of-contents)**
74 |
75 | ## References
76 |
77 | - [2.1](#2.1) Use `const` for all of your references; avoid using `var`.
78 |
79 | > Why? This ensures that you can't reassign your references (mutation), which can lead to bugs and difficult to comprehend code.
80 |
81 | ```javascript
82 | // bad
83 | var a = 1;
84 | var b = 2;
85 |
86 | // good
87 | const a = 1;
88 | const b = 2;
89 | ```
90 |
91 | - [2.2](#2.2) If you must mutate references, use `let` instead of `var`.
92 |
93 | > Why? `let` is block-scoped rather than function-scoped like `var`.
94 |
95 | ```javascript
96 | // bad
97 | var count = 1;
98 | if (true) {
99 |
100 | count += 1;
101 |
102 | }
103 |
104 | // good, use the let.
105 | let count = 1;
106 | if (true) {
107 |
108 | count += 1;
109 |
110 | }
111 | ```
112 |
113 | - [2.3](#2.3) Note that both `let` and `const` are block-scoped.
114 |
115 | ```javascript
116 | // const and let only exist in the blocks they are defined in.
117 | {
118 | let a = 1;
119 | const b = 1;
120 | }
121 | console.log(a); // ReferenceError
122 | console.log(b); // ReferenceError
123 | ```
124 |
125 | **[⬆ back to top](#table-of-contents)**
126 |
127 | ## Objects
128 |
129 | - [3.1](#3.1) Use the literal syntax for object creation.
130 |
131 | ```javascript
132 | // bad
133 | const item = new Object();
134 |
135 | // good
136 | const item = {};
137 | ```
138 |
139 | - [3.2](#3.2) Don't use [reserved words](http://es5.github.io/#x7.6.1) as keys. It won't work in IE8. [More info](https://github.com/airbnb/javascript/issues/61).
140 |
141 | ```javascript
142 | // bad
143 | const superman = {
144 | default: { clark: 'kent' },
145 | private: true,
146 | };
147 |
148 | // good
149 | const superman = {
150 | defaults: { clark: 'kent' },
151 | hidden: true,
152 | };
153 | ```
154 |
155 | - [3.3](#3.3) Use readable synonyms in place of reserved words.
156 |
157 | ```javascript
158 | // bad
159 | const superman = {
160 | class: 'alien',
161 | };
162 |
163 | // bad
164 | const superman = {
165 | klass: 'alien',
166 | };
167 |
168 | // good
169 | const superman = {
170 | type: 'alien',
171 | };
172 | ```
173 |
174 |
175 | - [3.4](#3.4) Use computed property names when creating objects with dynamic property names.
176 |
177 | > Why? They allow you to define all the properties of an object in one place.
178 |
179 | ```javascript
180 |
181 | const getKey = function(k) {
182 |
183 | return `a key named ${k}`;
184 |
185 | }
186 |
187 | // bad
188 | const obj = {
189 | id: 5,
190 | name: 'San Francisco',
191 | };
192 | obj[getKey('enabled')] = true;
193 |
194 | // good
195 | const obj = {
196 | id: 5,
197 | name: 'San Francisco',
198 | [getKey('enabled')]: true,
199 | };
200 | ```
201 |
202 |
203 | - [3.5](#3.5) Use arrow functions for object methods instead of shorthand properties or an anonymous function.
204 |
205 | ```javascript
206 | // bad
207 | const atom = {
208 | value: 1,
209 | addValue: function (value) {
210 | return atom.value + value;
211 | },
212 | };
213 |
214 | // bad
215 | const atom = {
216 | value: 1,
217 | addValue(value) {
218 | return atom.value + value;
219 | },
220 | };
221 |
222 | // good
223 | const atom = {
224 | value: 1,
225 | addValue: (value) => atom.value + value
226 | };
227 | ```
228 |
229 |
230 | - [3.6](#3.6) Use property value shorthand.
231 |
232 | > Why? It is shorter to write and descriptive.
233 |
234 | ```javascript
235 | const lukeSkywalker = 'Luke Skywalker';
236 |
237 | // bad
238 | const obj = {
239 | lukeSkywalker: lukeSkywalker,
240 | };
241 |
242 | // good
243 | const obj = {
244 | lukeSkywalker,
245 | };
246 | ```
247 |
248 | - [3.7](#3.7) Group your shorthand properties at the beginning of your object declaration.
249 |
250 | > Why? It's easier to tell which properties are using the shorthand.
251 |
252 | ```javascript
253 | const anakinSkywalker = 'Anakin Skywalker';
254 | const lukeSkywalker = 'Luke Skywalker';
255 |
256 | // bad
257 | const obj = {
258 | episodeOne: 1,
259 | twoJedisWalkIntoACantina: 2,
260 | lukeSkywalker,
261 | episodeThree: 3,
262 | mayTheFourth: 4,
263 | anakinSkywalker,
264 | };
265 |
266 | // good
267 | const obj = {
268 | lukeSkywalker,
269 | anakinSkywalker,
270 | episodeOne: 1,
271 | twoJedisWalkIntoACantina: 2,
272 | episodeThree: 3,
273 | mayTheFourth: 4,
274 | };
275 | ```
276 |
277 | **[⬆ back to top](#table-of-contents)**
278 |
279 | ## Arrays
280 |
281 | - [4.1](#4.1) Use the literal syntax for array creation.
282 |
283 | ```javascript
284 | // bad
285 | const items = new Array();
286 |
287 | // good
288 | const items = [];
289 | ```
290 |
291 | - [4.2](#4.2) Use Array#push instead of direct assignment to add items to an array.
292 |
293 | ```javascript
294 | const someStack = [];
295 |
296 |
297 | // bad
298 | someStack[someStack.length] = 'abracadabra';
299 |
300 | // good
301 | someStack.push('abracadabra');
302 | ```
303 |
304 |
305 | - [4.3](#4.3) Use array spreads `...` to copy arrays.
306 |
307 | ```javascript
308 | // bad
309 | const len = items.length;
310 | const itemsCopy = [];
311 | let i;
312 |
313 | for (i = 0; i < len; i++) {
314 | itemsCopy[i] = items[i];
315 | }
316 |
317 | // good
318 | const itemsCopy = [...items];
319 | ```
320 | - [4.4](#4.4) To convert an array-like object to an array, use Array#from.
321 |
322 | ```javascript
323 | const foo = document.querySelectorAll('.foo');
324 | const nodes = Array.from(foo);
325 | ```
326 |
327 | **[⬆ back to top](#table-of-contents)**
328 |
329 | ## Destructuring
330 |
331 | - [5.1](#5.1) Use object destructuring when accessing and using multiple properties of an object.
332 |
333 | > Why? Destructuring saves you from creating temporary references for those properties.
334 |
335 | ```javascript
336 | // bad
337 | const getFullName = function(user) {
338 |
339 | const firstName = user.firstName;
340 | const lastName = user.lastName;
341 |
342 | return `${firstName} ${lastName}`;
343 |
344 | }
345 |
346 | // good
347 | const getFullName = function(obj) {
348 |
349 | const { firstName, lastName } = obj;
350 | return `${firstName} ${lastName}`;
351 |
352 | }
353 |
354 | // best
355 | const getFullName = function({ firstName, lastName }) {
356 |
357 | return `${firstName} ${lastName}`;
358 |
359 | }
360 | ```
361 |
362 | - [5.2](#5.2) Use array destructuring.
363 |
364 | ```javascript
365 | const arr = [1, 2, 3, 4];
366 |
367 | // bad
368 | const first = arr[0];
369 | const second = arr[1];
370 |
371 | // good
372 | const [first, second] = arr;
373 | ```
374 |
375 | - [5.3](#5.3) Use object destructuring for multiple return values, not array destructuring.
376 |
377 | > Why? You can add new properties over time or change the order of things without breaking call sites.
378 |
379 | ```javascript
380 | // bad
381 | const processInput = function(input) {
382 | // then a miracle occurs
383 | return [left, right, top, bottom];
384 |
385 | }
386 |
387 | // the caller needs to think about the order of return data
388 | const [left, __, top] = processInput(input);
389 |
390 | // good
391 | const processInput = function(input) {
392 | // then a miracle occurs
393 | return { left, right, top, bottom };
394 |
395 | }
396 |
397 | // the caller selects only the data they need
398 | const { left, right } = processInput(input);
399 | ```
400 |
401 |
402 | **[⬆ back to top](#table-of-contents)**
403 |
404 | ## Strings
405 |
406 | - [6.1](#6.1) Use single quotes `''` for strings.
407 |
408 | ```javascript
409 | // bad
410 | const name = "Capt. Janeway";
411 |
412 | // good
413 | const name = 'Capt. Janeway';
414 | ```
415 |
416 | - [6.2](#6.2) Strings longer than 80 characters should be written across multiple lines using string concatenation.
417 | - [6.3](#6.3) Note: If overused, long strings with concatenation could impact performance. [jsPerf](http://jsperf.com/ya-string-concat) & [Discussion](https://github.com/airbnb/javascript/issues/40).
418 |
419 | ```javascript
420 | // bad
421 | const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
422 |
423 | // bad
424 | const errorMessage = 'This is a super long error that was thrown because \
425 | of Batman. When you stop to think about how Batman had anything to do \
426 | with this, you would get nowhere \
427 | fast.';
428 |
429 | // good
430 | const errorMessage = 'This is a super long error that was thrown because ' +
431 | 'of Batman. When you stop to think about how Batman had anything to do ' +
432 | 'with this, you would get nowhere fast.';
433 | ```
434 |
435 |
436 | - [6.4](#6.4) When programmatically building up strings, use template strings instead of concatenation.
437 |
438 | > Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features.
439 |
440 | ```javascript
441 | // bad
442 | const sayHi = function(name) {
443 |
444 | return 'How are you, ' + name + '?';
445 |
446 | }
447 |
448 | // bad
449 | const sayHi = function(name) {
450 |
451 | return ['How are you, ', name, '?'].join();
452 |
453 | }
454 |
455 | // good
456 | const sayHi = function(name) {
457 |
458 | return `How are you, ${name}?`;
459 |
460 | }
461 | ```
462 |
463 | **[⬆ back to top](#table-of-contents)**
464 |
465 |
466 | ## Functions
467 |
468 | - [7.1](#7.1) Use function expressions instead of function declarations.
469 |
470 | > Why? Badly placed Function Declarations are misleading and there are few (if any) situations where you can’t use a Function Expression assigned to a variable instead. See [function-declarations-vs-function-expressions](https://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/).
471 |
472 | ```javascript
473 | // bad
474 | function foo() {
475 | }
476 |
477 | // good
478 | const foo = function() {
479 | };
480 |
481 | // good
482 | const foo = () => {
483 | };
484 | ```
485 |
486 | - [7.2](#7.2) Function expressions:
487 |
488 | ```javascript
489 | // immediately-invoked function expression (IIFE)
490 | (() => {
491 | console.log('Welcome to the Internet. Please follow me.');
492 | })();
493 | ```
494 |
495 | - [7.3](#7.3) Never declare a function in a non-function block (if, while, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears.
496 | - [7.4](#7.4) **Note:** ECMA-262 defines a `block` as a list of statements. A function declaration is not a statement. [Read ECMA-262's note on this issue](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97).
497 |
498 | ```javascript
499 | // bad
500 | if (currentUser) {
501 |
502 | const test = function() {
503 |
504 | console.log('Nope.');
505 |
506 | }
507 |
508 | }
509 |
510 | // good
511 | let test;
512 | if (currentUser) {
513 |
514 | test = () => {
515 |
516 | console.log('Yup.');
517 |
518 | };
519 |
520 | }
521 | ```
522 |
523 | - [7.5](#7.5) Never name a parameter `arguments`. This will take precedence over the `arguments` object that is given to every function scope.
524 |
525 | ```javascript
526 | // bad
527 | const nope = function(name, options, arguments) {
528 | // ...stuff...
529 | }
530 |
531 | // good
532 | const yup = function(name, options, args) {
533 | // ...stuff...
534 | }
535 | ```
536 |
537 |
538 | - [7.6](#7.6) Never use `arguments`, opt to use rest syntax `...` instead.
539 |
540 | > Why? `...` is explicit about which arguments you want pulled. Plus rest arguments are a real Array and not Array-like like `arguments`.
541 |
542 | ```javascript
543 | // bad
544 | const concatenateAll = function() {
545 |
546 | const args = Array.prototype.slice.call(arguments);
547 | return args.join('');
548 |
549 | }
550 |
551 | // good
552 | const concatenateAll = function(...args) {
553 |
554 | return args.join('');
555 |
556 | }
557 | ```
558 |
559 |
560 | - [7.7](#7.7) Use default parameter syntax rather than mutating function arguments.
561 |
562 | ```javascript
563 | // really bad
564 | const handleThings = function(opts) {
565 | // No! We shouldn't mutate function arguments.
566 | // Double bad: if opts is falsy it'll be set to an object which may
567 | // be what you want but it can introduce subtle bugs.
568 | opts = opts || {};
569 | // ...
570 | }
571 |
572 | // still bad
573 | const handleThings = function(opts) {
574 |
575 | if (opts === void 0) {
576 |
577 | opts = {};
578 |
579 | }
580 | // ...
581 | }
582 |
583 | // good
584 | const handleThings = function(opts = {}) {
585 | // ...
586 | }
587 | ```
588 |
589 | - [7.8](#7.8) Avoid side effects with default parameters
590 |
591 | > Why? They are confusing to reason about.
592 |
593 | ```javascript
594 | var b = 1;
595 | // bad
596 | const count = function(a = b++) {
597 |
598 | console.log(a);
599 |
600 | }
601 | count(); // 1
602 | count(); // 2
603 | count(3); // 3
604 | count(); // 3
605 | ```
606 |
607 |
608 | **[⬆ back to top](#table-of-contents)**
609 |
610 | ## Arrow Functions
611 |
612 | - [8.1](#8.1) When you must use function expressions (as when passing an anonymous function), use arrow function notation.
613 |
614 | > Why? It creates a version of the function that executes in the context of `this`, which is usually what you want, and is a more concise syntax.
615 |
616 | > Why not? If you have a fairly complicated function, you might move that logic out into its own function declaration.
617 |
618 | ```javascript
619 | // bad
620 | [1, 2, 3].map(function (x) {
621 |
622 | return x * x;
623 |
624 | });
625 |
626 | // good
627 | [1, 2, 3].map((x) => {
628 |
629 | return x * x;
630 |
631 | });
632 |
633 | // good
634 | [1, 2, 3].map((x) => x * x;);
635 | ```
636 |
637 | - [8.2](#8.2) If the function body fits on one line and there is only a single argument, feel free to omit the braces and parentheses, and use the implicit return. Otherwise, add the parentheses, braces, and use a `return` statement.
638 |
639 | > Why? Syntactic sugar. It reads well when multiple functions are chained together.
640 |
641 | > Why not? If you plan on returning an object.
642 |
643 | ```javascript
644 | // good
645 | [1, 2, 3].map(x => x * x);
646 |
647 | // good
648 | [1, 2, 3].reduce((total, n) => {
649 | return total + n;
650 | }, 0);
651 | ```
652 |
653 | **[⬆ back to top](#table-of-contents)**
654 |
655 |
656 | ## Constructors
657 |
658 | - [9.1](#9.1) Always use `class`. Avoid manipulating `prototype` directly.
659 |
660 | > Why? `class` syntax is more concise and easier to reason about.
661 |
662 | ```javascript
663 | // bad
664 | function Queue(contents = []) {
665 |
666 | this._queue = [...contents];
667 |
668 | }
669 | Queue.prototype.pop = function() {
670 |
671 | const value = this._queue[0];
672 | this._queue.splice(0, 1);
673 | return value;
674 |
675 | }
676 |
677 |
678 | // good
679 | class Queue {
680 |
681 | constructor(contents = []) {
682 |
683 | this._queue = [...contents];
684 |
685 | }
686 |
687 | pop() {
688 |
689 | const value = this._queue[0];
690 | this._queue.splice(0, 1);
691 | return value;
692 |
693 | }
694 |
695 | }
696 | ```
697 |
698 | - [9.2](#9.2) Use `extends` for inheritance.
699 |
700 | > Why? It is a built-in way to inherit prototype functionality without breaking `instanceof`.
701 |
702 | ```javascript
703 | // bad
704 | const inherits = require('inherits');
705 | function PeekableQueue(contents) {
706 |
707 | Queue.apply(this, contents);
708 |
709 | }
710 | inherits(PeekableQueue, Queue);
711 | PeekableQueue.prototype.peek = function() {
712 |
713 | return this._queue[0];
714 |
715 | }
716 |
717 | // good
718 | class PeekableQueue extends Queue {
719 |
720 | peek() {
721 |
722 | return this._queue[0];
723 |
724 | }
725 |
726 | }
727 | ```
728 |
729 | - [9.3](#9.3) Methods can return `this` to help with method chaining.
730 |
731 | ```javascript
732 | // bad
733 | Jedi.prototype.jump = function() {
734 |
735 | this.jumping = true;
736 | return true;
737 |
738 | };
739 |
740 | Jedi.prototype.setHeight = function(height) {
741 |
742 | this.height = height;
743 |
744 | };
745 |
746 | const luke = new Jedi();
747 | luke.jump(); // => true
748 | luke.setHeight(20); // => undefined
749 |
750 | // good
751 | class Jedi {
752 |
753 | jump() {
754 |
755 | this.jumping = true;
756 | return this;
757 |
758 | }
759 |
760 | setHeight(height) {
761 |
762 | this.height = height;
763 | return this;
764 |
765 | }
766 |
767 | }
768 |
769 | const luke = new Jedi();
770 |
771 | luke.jump()
772 | .setHeight(20);
773 | ```
774 |
775 |
776 | - [9.4](#9.4) It's okay to write a custom toString() method, just make sure it works successfully and causes no side effects.
777 |
778 | ```javascript
779 | class Jedi {
780 |
781 | contructor(options = {}) {
782 |
783 | this.name = options.name || 'no name';
784 |
785 | }
786 |
787 | getName() {
788 |
789 | return this.name;
790 |
791 | }
792 |
793 | toString() {
794 |
795 | return `Jedi - ${this.getName()}`;
796 |
797 | }
798 |
799 | }
800 | ```
801 |
802 |
803 | - [9.5](#9.5) Typescript classes placeholder.
804 |
805 | **[⬆ back to top](#table-of-contents)**
806 |
807 |
808 | ## Modules
809 |
810 | - [10.1](#10.1) Use modules (`import`/`export`) over a non-standard module system.
811 |
812 | > Why? Modules are the future, let's start using the future now.
813 |
814 | ```javascript
815 | // bad
816 | const AirbnbStyleGuide = require('./AirbnbStyleGuide');
817 | module.exports = AirbnbStyleGuide.es6;
818 |
819 | // ok
820 | import AirbnbStyleGuide from './AirbnbStyleGuide';
821 | export default AirbnbStyleGuide.es6;
822 |
823 | // best
824 | import { es6 } from './AirbnbStyleGuide';
825 | export default es6;
826 | ```
827 |
828 | - [10.2](#10.2) And do not export directly from an import.
829 |
830 | > Why? Although the one-liner is concise, having one clear way to import and one clear way to export makes things consistent.
831 |
832 | ```javascript
833 | // bad
834 | // filename es6.js
835 | export { es6 as default } from './airbnbStyleGuide';
836 |
837 | // good
838 | // filename es6.js
839 | import { es6 } from './AirbnbStyleGuide';
840 | export default es6;
841 | ```
842 |
843 | - [10.3](#10.3) Use TypeScript module import for non-ES6 libraries with type definitions. Check [DefinitelyTyped](https://github.com/borisyankov/DefinitelyTyped) for available type definition files.
844 |
845 | > Why? This provides type information from external modules when available
846 |
847 | ```javascript
848 | // bad
849 | ///
850 | var lodash = require('lodash')
851 |
852 | // good
853 | ///
854 | import lodash = require('lodash')
855 | ```
856 |
857 | - [10.4](#10.4) Group module imports by type and then alphabetic by variable name. Follow these rules for ordering your module imports:
858 | + External libraries with type definitions
859 | + Internal typescript modules with wildcard imports
860 | + Internal typescript modules without wildcard imports
861 | + External libraries without type definitions
862 |
863 |
864 | > Why? This makes your import section consistent across all modules.
865 |
866 | ```javascript
867 | // bad
868 | ///
869 | import * as Api from './api';
870 | import _ = require('lodash');
871 | var Distillery = require('distillery-js');
872 | import Partner from './partner';
873 | import * as Util from './util';
874 | import Q = require('Q');
875 | var request = require('request');
876 | import Customer from './customer';
877 |
878 | // good
879 | ///
880 | import _ = require('lodash');
881 | import Q = require('Q');
882 | import * as Api from './api';
883 | import * as Util from './util';
884 | import Customer from './customer';
885 | import Partner from './partner';
886 | var Distillery = require('distillery-js');
887 | var request = require('request');
888 | ```
889 |
890 | **[⬆ back to top](#table-of-contents)**
891 |
892 | ## Iterators and Generators
893 |
894 | - [11.1](#11.1) Don't use iterators. Prefer JavaScript's higher-order functions like `map()` and `reduce()` instead of loops like `for-of`.
895 |
896 | > Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side-effects.
897 |
898 | ```javascript
899 | const numbers = [1, 2, 3, 4, 5];
900 |
901 | // bad
902 | let sum = 0;
903 | for (let num of numbers) {
904 |
905 | sum += num;
906 |
907 | }
908 |
909 | sum === 15;
910 |
911 | // good
912 | let sum = 0;
913 | numbers.forEach((num) => sum += num);
914 | sum === 15;
915 |
916 | // best (use the functional force)
917 | const sum = numbers.reduce((total, num) => total + num, 0);
918 | sum === 15;
919 | ```
920 |
921 | - [11.2](#11.2) Don't use generators for now.
922 |
923 | > Why? They don't transpile well to ES5.
924 |
925 | **[⬆ back to top](#table-of-contents)**
926 |
927 |
928 | ## Properties
929 |
930 | - [12.1](#12.1) Use dot notation when accessing properties.
931 |
932 | ```javascript
933 | const luke = {
934 | jedi: true,
935 | age: 28,
936 | };
937 |
938 | // bad
939 | const isJedi = luke['jedi'];
940 |
941 | // good
942 | const isJedi = luke.jedi;
943 | ```
944 |
945 | - [12.2](#12.2) Use subscript notation `[]` when accessing properties with a variable.
946 |
947 | ```javascript
948 | const luke = {
949 | jedi: true,
950 | age: 28,
951 | };
952 |
953 | const getProp = function(prop) {
954 |
955 | return luke[prop];
956 |
957 | }
958 |
959 | const isJedi = getProp('jedi');
960 | ```
961 |
962 | **[⬆ back to top](#table-of-contents)**
963 |
964 |
965 | ## Variables
966 |
967 | - [13.1](#13.1) Always use `const` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that.
968 |
969 | ```javascript
970 | // bad
971 | superPower = new SuperPower();
972 |
973 | // good
974 | const superPower = new SuperPower();
975 | ```
976 |
977 | - [13.2](#13.2) Use one `const` declaration per variable.
978 |
979 | > Why? It's easier to add new variable declarations this way, and you never have to worry about swapping out a `;` for a `,` or introducing punctuation-only diffs.
980 |
981 | ```javascript
982 | // bad
983 | const items = getItems(),
984 | goSportsTeam = true,
985 | dragonball = 'z';
986 |
987 | // bad
988 | // (compare to above, and try to spot the mistake)
989 | const items = getItems(),
990 | goSportsTeam = true;
991 | dragonball = 'z';
992 |
993 | // good
994 | const items = getItems();
995 | const goSportsTeam = true;
996 | const dragonball = 'z';
997 | ```
998 |
999 | - [13.3](#13.3) Group all your `const`s and then group all your `let`s.
1000 |
1001 | > Why? This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables.
1002 |
1003 | ```javascript
1004 | // bad
1005 | let i, len, dragonball,
1006 | items = getItems(),
1007 | goSportsTeam = true;
1008 |
1009 | // bad
1010 | let i;
1011 | const items = getItems();
1012 | let dragonball;
1013 | const goSportsTeam = true;
1014 | let len;
1015 |
1016 | // good
1017 | const goSportsTeam = true;
1018 | const items = getItems();
1019 | let dragonball;
1020 | let i;
1021 | let length;
1022 | ```
1023 |
1024 | - [13.4](#13.4) Assign variables where you need them, but place them in a reasonable place.
1025 |
1026 | > Why? `let` and `const` are block scoped and not function scoped.
1027 |
1028 | ```javascript
1029 | // good
1030 | function() {
1031 |
1032 | test();
1033 | console.log('doing stuff..');
1034 |
1035 | //..other stuff..
1036 |
1037 | const name = getName();
1038 |
1039 | if (name === 'test') {
1040 |
1041 | return false;
1042 |
1043 | }
1044 |
1045 | return name;
1046 |
1047 | }
1048 |
1049 | // bad - unnessary function call
1050 | function(hasName) {
1051 |
1052 | const name = getName();
1053 |
1054 | if (!hasName) {
1055 |
1056 | return false;
1057 |
1058 | }
1059 |
1060 | this.setFirstName(name);
1061 |
1062 | return true;
1063 |
1064 | }
1065 |
1066 | // good
1067 | function(hasName) {
1068 |
1069 | if (!hasName) {
1070 |
1071 | return false;
1072 |
1073 | }
1074 |
1075 | const name = getName();
1076 | this.setFirstName(name);
1077 |
1078 | return true;
1079 |
1080 | }
1081 | ```
1082 |
1083 | **[⬆ back to top](#table-of-contents)**
1084 |
1085 |
1086 | ## Hoisting
1087 |
1088 | - [14.1](#14.1) `var` declarations get hoisted to the top of their scope, their assignment does not. `const` and `let` declarations are blessed with a new concept called [Temporal Dead Zones (TDZ)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone_and_errors_with_let). It's important to know why [typeof is no longer safe](http://es-discourse.com/t/why-typeof-is-no-longer-safe/15).
1089 |
1090 | ```javascript
1091 | // we know this wouldn't work (assuming there
1092 | // is no notDefined global variable)
1093 | function example() {
1094 |
1095 | console.log(notDefined); // => throws a ReferenceError
1096 |
1097 | }
1098 |
1099 | // creating a variable declaration after you
1100 | // reference the variable will work due to
1101 | // variable hoisting. Note: the assignment
1102 | // value of `true` is not hoisted.
1103 | function example() {
1104 |
1105 | console.log(declaredButNotAssigned); // => undefined
1106 | var declaredButNotAssigned = true;
1107 |
1108 | }
1109 |
1110 | // The interpreter is hoisting the variable
1111 | // declaration to the top of the scope,
1112 | // which means our example could be rewritten as:
1113 | function example() {
1114 |
1115 | let declaredButNotAssigned;
1116 | console.log(declaredButNotAssigned); // => undefined
1117 | declaredButNotAssigned = true;
1118 |
1119 | }
1120 |
1121 | // using const and let
1122 | function example() {
1123 |
1124 | console.log(declaredButNotAssigned); // => throws a ReferenceError
1125 | console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
1126 | const declaredButNotAssigned = true;
1127 |
1128 | }
1129 | ```
1130 |
1131 | - [14.2](#14.2) Anonymous function expressions hoist their variable name, but not the function assignment.
1132 |
1133 | ```javascript
1134 | function example() {
1135 |
1136 | console.log(anonymous); // => undefined
1137 |
1138 | anonymous(); // => TypeError anonymous is not a function
1139 |
1140 | var anonymous = function() {
1141 |
1142 | console.log('anonymous function expression');
1143 |
1144 | };
1145 |
1146 | }
1147 | ```
1148 |
1149 | - [14.3](#14.3) Named function expressions hoist the variable name, not the function name or the function body.
1150 |
1151 | ```javascript
1152 | function example() {
1153 |
1154 | console.log(named); // => undefined
1155 |
1156 | named(); // => TypeError named is not a function
1157 |
1158 | superPower(); // => ReferenceError superPower is not defined
1159 |
1160 | var named = function superPower() {
1161 |
1162 | console.log('Flying');
1163 |
1164 | };
1165 |
1166 | }
1167 |
1168 | // the same is true when the function name
1169 | // is the same as the variable name.
1170 | function example() {
1171 |
1172 | console.log(named); // => undefined
1173 |
1174 | named(); // => TypeError named is not a function
1175 |
1176 | var named = function named() {
1177 |
1178 | console.log('named');
1179 |
1180 | }
1181 |
1182 | }
1183 | ```
1184 |
1185 | - [14.4](#14.4) Function declarations hoist their name and the function body.
1186 |
1187 | ```javascript
1188 | function example() {
1189 |
1190 | superPower(); // => Flying
1191 |
1192 | function superPower() {
1193 |
1194 | console.log('Flying');
1195 |
1196 | }
1197 |
1198 | }
1199 | ```
1200 |
1201 | - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/).
1202 |
1203 | **[⬆ back to top](#table-of-contents)**
1204 |
1205 |
1206 | ## Comparison Operators & Equality
1207 |
1208 | - [15.1](#15.1) Use `===` and `!==` over `==` and `!=`.
1209 | - [15.2](#15.2) Conditional statements such as the `if` statement evaulate their expression using coercion with the `ToBoolean` abstract method and always follow these simple rules:
1210 |
1211 | + **Objects** evaluate to **true**
1212 | + **Undefined** evaluates to **false**
1213 | + **Null** evaluates to **false**
1214 | + **Booleans** evaluate to **the value of the boolean**
1215 | + **Numbers** evaluate to **false** if **+0, -0, or NaN**, otherwise **true**
1216 | + **Strings** evaluate to **false** if an empty string `''`, otherwise **true**
1217 |
1218 | ```javascript
1219 | if ([0]) {
1220 | // true
1221 | // An array is an object, objects evaluate to true
1222 | }
1223 | ```
1224 |
1225 | - [15.3](#15.3) Use shortcuts.
1226 |
1227 | ```javascript
1228 | // bad
1229 | if (name !== '') {
1230 | // ...stuff...
1231 | }
1232 |
1233 | // good
1234 | if (name) {
1235 | // ...stuff...
1236 | }
1237 |
1238 | // bad
1239 | if (collection.length > 0) {
1240 | // ...stuff...
1241 | }
1242 |
1243 | // good
1244 | if (collection.length) {
1245 | // ...stuff...
1246 | }
1247 | ```
1248 |
1249 | - [15.4](#15.4) For more information see [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll.
1250 |
1251 | **[⬆ back to top](#table-of-contents)**
1252 |
1253 |
1254 | ## Blocks
1255 |
1256 | - [16.1](#16.1) Use braces with multi-line blocks or omit braces for two line blocks.
1257 |
1258 | ```javascript
1259 | // bad
1260 | if (test) return false;
1261 |
1262 | // ok
1263 | if (test)
1264 | return false;
1265 |
1266 | // good
1267 | if (test) {
1268 |
1269 | return false;
1270 |
1271 | }
1272 |
1273 | // bad
1274 | function() { return false; }
1275 |
1276 | // good
1277 | function() {
1278 |
1279 | return false;
1280 |
1281 | }
1282 | ```
1283 |
1284 | - [16.2](#16.2) If you're using multi-line blocks with `if` and `else`, put `else` on the same line as your
1285 | `if` block's closing brace.
1286 |
1287 | ```javascript
1288 | // bad
1289 | if (test) {
1290 | thing1();
1291 | thing2();
1292 | }
1293 | else {
1294 | thing3();
1295 | }
1296 |
1297 | // good
1298 | if (test) {
1299 | thing1();
1300 | thing2();
1301 | } else {
1302 | thing3();
1303 | }
1304 | ```
1305 |
1306 | - [16.3](#16.3) If you're using multi-line blocks with `if` and `else`, do not omit curly braces.
1307 |
1308 | > Why? Omitting curly braces in multi-line blocks can easily cause unexpected behavior.
1309 |
1310 | ```javascript
1311 | // bad
1312 | if (test)
1313 | thing1();
1314 | thing2();
1315 | else
1316 | thing3();
1317 |
1318 | // good
1319 | if (test) {
1320 | thing1();
1321 | thing2();
1322 | } else {
1323 | thing3();
1324 | }
1325 | ```
1326 |
1327 |
1328 | **[⬆ back to top](#table-of-contents)**
1329 |
1330 |
1331 | ## Comments
1332 |
1333 | - [17.1](#17.1) Use `/** ... */` for multi-line comments. Include a description, specify types and values for all parameters and return values.
1334 |
1335 | ```javascript
1336 | // bad
1337 | // make() returns a new element
1338 | // based on the passed in tag name
1339 | //
1340 | // @param {String} tag
1341 | // @return {Element} element
1342 | const make = function(tag) {
1343 |
1344 | // ...stuff...
1345 |
1346 | return element;
1347 |
1348 | }
1349 |
1350 | // good
1351 | /**
1352 | * make() returns a new element
1353 | * based on the passed in tag name
1354 | *
1355 | * @param {String} tag
1356 | * @return {Element} element
1357 | */
1358 | const make = function(tag) {
1359 |
1360 | // ...stuff...
1361 |
1362 | return element;
1363 |
1364 | }
1365 | ```
1366 |
1367 | - [17.2](#17.2) Use `//` for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment.
1368 |
1369 | ```javascript
1370 | // bad
1371 | const active = true; // is current tab
1372 |
1373 | // good
1374 | // is current tab
1375 | const active = true;
1376 |
1377 | // bad
1378 | const getType = function() {
1379 |
1380 | console.log('fetching type...');
1381 | // set the default type to 'no type'
1382 | const type = this._type || 'no type';
1383 |
1384 | return type;
1385 |
1386 | }
1387 |
1388 | // good
1389 | const getType = function() {
1390 |
1391 | console.log('fetching type...');
1392 |
1393 | // set the default type to 'no type'
1394 | const type = this._type || 'no type';
1395 |
1396 | return type;
1397 |
1398 | }
1399 | ```
1400 |
1401 | - [17.3](#17.3) Prefixing your comments with `FIXME` or `TODO` helps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are `FIXME -- need to figure this out` or `TODO -- need to implement`.
1402 |
1403 | - [17.4](#17.4) Use `// FIXME:` to annotate problems.
1404 |
1405 | ```javascript
1406 | class Calculator {
1407 |
1408 | constructor() {
1409 | // FIXME: shouldn't use a global here
1410 | total = 0;
1411 | }
1412 |
1413 | }
1414 | ```
1415 |
1416 | - [17.5](#17.5) Use `// TODO:` to annotate solutions to problems.
1417 |
1418 | ```javascript
1419 | class Calculator {
1420 |
1421 | constructor() {
1422 | // TODO: total should be configurable by an options param
1423 | this.total = 0;
1424 | }
1425 |
1426 | }
1427 | ```
1428 |
1429 | **[⬆ back to top](#table-of-contents)**
1430 |
1431 |
1432 | ## Whitespace
1433 |
1434 | - [18.1](#18.1) Use soft tabs set to 2 spaces.
1435 |
1436 | ```javascript
1437 | // bad
1438 | function() {
1439 |
1440 | ∙∙∙∙const name;
1441 |
1442 | }
1443 |
1444 | // bad
1445 | function() {
1446 |
1447 | ∙const name;
1448 |
1449 | }
1450 |
1451 | // good
1452 | function() {
1453 |
1454 | ∙∙const name;
1455 |
1456 | }
1457 | ```
1458 |
1459 | - [18.2](#18.2) Place 1 space before the leading brace.
1460 |
1461 | ```javascript
1462 | // bad
1463 | const test = function(){
1464 |
1465 | console.log('test');
1466 |
1467 | }
1468 |
1469 | // good
1470 | const test = function() {
1471 |
1472 | console.log('test');
1473 |
1474 | }
1475 |
1476 | // bad
1477 | dog.set('attr',{
1478 | age: '1 year',
1479 | breed: 'Bernese Mountain Dog',
1480 | });
1481 |
1482 | // good
1483 | dog.set('attr', {
1484 | age: '1 year',
1485 | breed: 'Bernese Mountain Dog',
1486 | });
1487 | ```
1488 |
1489 | - [18.3](#18.3) Place 1 space before the opening parenthesis in control statements (`if`, `while` etc.). Place no space before the argument list in function calls and declarations.
1490 |
1491 | ```javascript
1492 | // bad
1493 | if(isJedi) {
1494 |
1495 | fight ();
1496 |
1497 | }
1498 |
1499 | // good
1500 | if (isJedi) {
1501 |
1502 | fight();
1503 |
1504 | }
1505 |
1506 | // bad
1507 | const fight = function () {
1508 |
1509 | console.log ('Swooosh!');
1510 |
1511 | }
1512 |
1513 | // good
1514 | const fight = function() {
1515 |
1516 | console.log('Swooosh!');
1517 |
1518 | }
1519 | ```
1520 |
1521 | - [18.4](#18.4) Set off operators with spaces.
1522 |
1523 | ```javascript
1524 | // bad
1525 | const x=y+5;
1526 |
1527 | // good
1528 | const x = y + 5;
1529 | ```
1530 |
1531 | - [18.5](#18.5) End files with a single newline character.
1532 |
1533 | ```javascript
1534 | // bad
1535 | (function(global) {
1536 | // ...stuff...
1537 | })(this);
1538 | ```
1539 |
1540 | ```javascript
1541 | // bad
1542 | (function(global) {
1543 | // ...stuff...
1544 | })(this);↵
1545 | ↵
1546 | ```
1547 |
1548 | ```javascript
1549 | // good
1550 | (function(global) {
1551 | // ...stuff...
1552 | })(this);↵
1553 | ```
1554 |
1555 | - [18.5](#18.5) Use indentation when making long method chains. Use a leading dot, which
1556 | emphasizes that the line is a method call, not a new statement.
1557 |
1558 | ```javascript
1559 | // bad
1560 | $('#items').find('.selected').highlight().end().find('.open').updateCount();
1561 |
1562 | // bad
1563 | $('#items').
1564 | find('.selected').
1565 | highlight().
1566 | end().
1567 | find('.open').
1568 | updateCount();
1569 |
1570 | // good
1571 | $('#items')
1572 | .find('.selected')
1573 | .highlight()
1574 | .end()
1575 | .find('.open')
1576 | .updateCount();
1577 |
1578 | // bad
1579 | const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true)
1580 | .attr('width', (radius + margin) * 2).append('svg:g')
1581 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
1582 | .call(tron.led);
1583 |
1584 | // good
1585 | const leds = stage.selectAll('.led')
1586 | .data(data)
1587 | .enter().append('svg:svg')
1588 | .classed('led', true)
1589 | .attr('width', (radius + margin) * 2)
1590 | .append('svg:g')
1591 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
1592 | .call(tron.led);
1593 | ```
1594 |
1595 | - [18.6](#18.6) Leave a blank line after the opening of a block and before the closing of a block
1596 |
1597 | ```javascript
1598 | // bad
1599 | if (foo) {
1600 | return bar;
1601 | }
1602 |
1603 | // good
1604 | if (foo) {
1605 |
1606 | return bar;
1607 |
1608 | }
1609 |
1610 | // bad
1611 | const baz = function(foo) {
1612 | return bar;
1613 | }
1614 |
1615 | // good
1616 | const baz = function(foo) {
1617 |
1618 | return bar;
1619 |
1620 | }
1621 | ```
1622 |
1623 | - [18.7](#18.7) Leave a blank line after blocks and before the next statement.
1624 |
1625 | ```javascript
1626 | // bad
1627 | if (foo) {
1628 |
1629 | return bar;
1630 |
1631 | }
1632 | return baz;
1633 |
1634 | // good
1635 | if (foo) {
1636 |
1637 | return bar;
1638 |
1639 | }
1640 |
1641 | return baz;
1642 |
1643 | // bad
1644 | const obj = {
1645 | foo() {
1646 | },
1647 | bar() {
1648 | },
1649 | };
1650 | return obj;
1651 |
1652 | // good
1653 | const obj = {
1654 | foo() {
1655 | },
1656 |
1657 | bar() {
1658 | },
1659 | };
1660 |
1661 | return obj;
1662 | ```
1663 |
1664 |
1665 | **[⬆ back to top](#table-of-contents)**
1666 |
1667 | ## Commas
1668 |
1669 | - [19.1](#19.1) Leading commas: **Nope.**
1670 |
1671 | ```javascript
1672 | // bad
1673 | const story = [
1674 | once
1675 | , upon
1676 | , aTime
1677 | ];
1678 |
1679 | // good
1680 | const story = [
1681 | once,
1682 | upon,
1683 | aTime,
1684 | ];
1685 |
1686 | // bad
1687 | const hero = {
1688 | firstName: 'Ada'
1689 | , lastName: 'Lovelace'
1690 | , birthYear: 1815
1691 | , superPower: 'computers'
1692 | };
1693 |
1694 | // good
1695 | const hero = {
1696 | firstName: 'Ada',
1697 | lastName: 'Lovelace',
1698 | birthYear: 1815,
1699 | superPower: 'computers',
1700 | };
1701 | ```
1702 |
1703 | - [19.2](#19.2) Additional trailing comma: **Yup.**
1704 |
1705 | > Why? This leads to cleaner git diffs. Also, transpilers like Babel will remove the additional trailing comma in the transpiled code which means you don't have to worry about the [trailing comma problem](es5/README.md#commas) in legacy browsers.
1706 |
1707 | ```javascript
1708 | // bad - git diff without trailing comma
1709 | const hero = {
1710 | firstName: 'Florence',
1711 | - lastName: 'Nightingale'
1712 | + lastName: 'Nightingale',
1713 | + inventorOf: ['coxcomb graph', 'mordern nursing']
1714 | }
1715 |
1716 | // good - git diff with trailing comma
1717 | const hero = {
1718 | firstName: 'Florence',
1719 | lastName: 'Nightingale',
1720 | + inventorOf: ['coxcomb chart', 'mordern nursing'],
1721 | }
1722 |
1723 | // bad
1724 | const hero = {
1725 | firstName: 'Dana',
1726 | lastName: 'Scully'
1727 | };
1728 |
1729 | const heroes = [
1730 | 'Batman',
1731 | 'Superman'
1732 | ];
1733 |
1734 | // good
1735 | const hero = {
1736 | firstName: 'Dana',
1737 | lastName: 'Scully',
1738 | };
1739 |
1740 | const heroes = [
1741 | 'Batman',
1742 | 'Superman',
1743 | ];
1744 | ```
1745 |
1746 | **[⬆ back to top](#table-of-contents)**
1747 |
1748 |
1749 | ## Semicolons
1750 |
1751 | - [20.1](#20.1) **Yup.**
1752 |
1753 | ```javascript
1754 | // bad
1755 | (function() {
1756 |
1757 | const name = 'Skywalker'
1758 | return name
1759 |
1760 | })()
1761 |
1762 | // good
1763 | (() => {
1764 |
1765 | const name = 'Skywalker';
1766 | return name;
1767 |
1768 | })();
1769 |
1770 | // good (guards against the function becoming an argument when two files with IIFEs are concatenated)
1771 | ;(() => {
1772 |
1773 | const name = 'Skywalker';
1774 | return name;
1775 |
1776 | })();
1777 | ```
1778 |
1779 | [Read more](http://stackoverflow.com/a/7365214/1712802).
1780 |
1781 | **[⬆ back to top](#table-of-contents)**
1782 |
1783 |
1784 | ## Type Casting & Coercion
1785 |
1786 | - [21.1](#21.1) Perform type coercion at the beginning of the statement.
1787 | - [21.2](#21.2) Strings:
1788 |
1789 | ```javascript
1790 | // => this.reviewScore = 9;
1791 |
1792 | // bad
1793 | const totalScore = this.reviewScore + '';
1794 |
1795 | // good
1796 | const totalScore = String(this.reviewScore);
1797 | ```
1798 |
1799 | - [21.3](#21.3) Use `parseInt` for Numbers and always with a radix for type casting.
1800 |
1801 | ```javascript
1802 | const inputValue = '4';
1803 |
1804 | // bad
1805 | const val = new Number(inputValue);
1806 |
1807 | // bad
1808 | const val = +inputValue;
1809 |
1810 | // bad
1811 | const val = inputValue >> 0;
1812 |
1813 | // bad
1814 | const val = parseInt(inputValue);
1815 |
1816 | // good
1817 | const val = Number(inputValue);
1818 |
1819 | // good
1820 | const val = parseInt(inputValue, 10);
1821 | ```
1822 |
1823 | - [21.4](#21.4) If for whatever reason you are doing something wild and `parseInt` is your bottleneck and need to use Bitshift for [performance reasons](http://jsperf.com/coercion-vs-casting/3), leave a comment explaining why and what you're doing.
1824 |
1825 | ```javascript
1826 | // good
1827 | /**
1828 | * parseInt was the reason my code was slow.
1829 | * Bitshifting the String to coerce it to a
1830 | * Number made it a lot faster.
1831 | */
1832 | const val = inputValue >> 0;
1833 | ```
1834 |
1835 | - [21.5](#21.5) **Note:** Be careful when using bitshift operations. Numbers are represented as [64-bit values](http://es5.github.io/#x4.3.19), but Bitshift operations always return a 32-bit integer ([source](http://es5.github.io/#x11.7)). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. [Discussion](https://github.com/airbnb/javascript/issues/109). Largest signed 32-bit Int is 2,147,483,647:
1836 |
1837 | ```javascript
1838 | 2147483647 >> 0 //=> 2147483647
1839 | 2147483648 >> 0 //=> -2147483648
1840 | 2147483649 >> 0 //=> -2147483647
1841 | ```
1842 |
1843 | - [21.6](#21.6) Booleans:
1844 |
1845 | ```javascript
1846 | const age = 0;
1847 |
1848 | // bad
1849 | const hasAge = new Boolean(age);
1850 |
1851 | // good
1852 | const hasAge = Boolean(age);
1853 |
1854 | // good
1855 | const hasAge = !!age;
1856 | ```
1857 |
1858 | **[⬆ back to top](#table-of-contents)**
1859 |
1860 |
1861 | ## Naming Conventions
1862 |
1863 | - [22.1](#22.1) Avoid single letter names. Be descriptive with your naming.
1864 |
1865 | ```javascript
1866 | // bad
1867 | function q() {
1868 | // ...stuff...
1869 | }
1870 |
1871 | // good
1872 | function query() {
1873 | // ..stuff..
1874 | }
1875 | ```
1876 |
1877 | - [22.2](#22.2) Use camelCase when naming objects, functions, and instances.
1878 |
1879 | ```javascript
1880 | // bad
1881 | const OBJEcttsssss = {};
1882 | const this_is_my_object = {};
1883 | const c = function() {}
1884 |
1885 | // good
1886 | const thisIsMyObject = {};
1887 | const thisIsMyFunction = function() {}
1888 | ```
1889 |
1890 | - [22.3](#22.3) Use PascalCase when naming constructors, classes, modules, or interfaces.
1891 |
1892 | ```javascript
1893 | // bad
1894 | function user(options) {
1895 |
1896 | this.name = options.name;
1897 |
1898 | }
1899 |
1900 | const bad = new user({
1901 | name: 'nope',
1902 | });
1903 |
1904 | // good
1905 | module AperatureScience {
1906 |
1907 | class User {
1908 |
1909 | constructor(options) {
1910 |
1911 | this.name = options.name;
1912 |
1913 | }
1914 |
1915 | }
1916 |
1917 | }
1918 |
1919 | const good = new AperatureScience.User({
1920 | name: 'yup',
1921 | });
1922 | ```
1923 |
1924 | - [22.4](#22.4) Use snake_case when naming object properties.
1925 |
1926 | ```javascript
1927 | // bad
1928 | const panda = {
1929 | firstName: 'Mr.',
1930 | LastName: 'Panda'
1931 | }
1932 |
1933 | // good
1934 | const panda = {
1935 | first_name: 'Mr.',
1936 | Last_name: 'Panda'
1937 | }
1938 | ```
1939 |
1940 | - [22.5](#22.5) Use a leading underscore `_` when naming private properties.
1941 |
1942 | ```javascript
1943 | // bad
1944 | this.__firstName__ = 'Panda';
1945 | this.firstName_ = 'Panda';
1946 |
1947 | // good
1948 | this._firstName = 'Panda';
1949 | ```
1950 |
1951 | - [22.6](#22.6) Don't save references to `this`. Use arrow functions or Function#bind.
1952 |
1953 | ```javascript
1954 | // bad
1955 | function foo() {
1956 |
1957 | const self = this;
1958 | return function() {
1959 |
1960 | console.log(self);
1961 |
1962 | };
1963 |
1964 | }
1965 |
1966 | // bad
1967 | function foo() {
1968 |
1969 | const that = this;
1970 | return function() {
1971 |
1972 | console.log(that);
1973 |
1974 | };
1975 |
1976 | }
1977 |
1978 | // good
1979 | function foo() {
1980 |
1981 | return () => {
1982 | console.log(this);
1983 | };
1984 |
1985 | }
1986 | ```
1987 |
1988 | - [22.7](#22.7) If your file exports a single class, your filename should be exactly the name of the class.
1989 | ```javascript
1990 | // file contents
1991 | class CheckBox {
1992 | // ...
1993 | }
1994 | export default CheckBox;
1995 |
1996 | // in some other file
1997 | // bad
1998 | import CheckBox from './checkBox';
1999 |
2000 | // bad
2001 | import CheckBox from './check_box';
2002 |
2003 | // good
2004 | import CheckBox from './CheckBox';
2005 | ```
2006 |
2007 | - [22.8](#22.8) Use camelCase when you export-default a function. Your filename should be identical to your function's name.
2008 |
2009 | ```javascript
2010 | function makeStyleGuide() {
2011 | }
2012 |
2013 | export default makeStyleGuide;
2014 | ```
2015 |
2016 | - [22.9](#22.9) Use PascalCase when you export a singleton / function library / bare object.
2017 |
2018 | ```javascript
2019 | const AirbnbStyleGuide = {
2020 | es6: {
2021 | }
2022 | };
2023 |
2024 | export default AirbnbStyleGuide;
2025 | ```
2026 |
2027 |
2028 | **[⬆ back to top](#table-of-contents)**
2029 |
2030 |
2031 | ## Accessors
2032 |
2033 | - [23.1](#23.1) Accessor functions for properties are not required.
2034 | - [23.2](#23.2) If you do make accessor functions use getVal() and setVal('hello').
2035 |
2036 | ```javascript
2037 | // bad
2038 | dragon.age();
2039 |
2040 | // good
2041 | dragon.getAge();
2042 |
2043 | // bad
2044 | dragon.age(25);
2045 |
2046 | // good
2047 | dragon.setAge(25);
2048 | ```
2049 |
2050 | - [23.3](#23.3) If the property is a boolean, use isVal() or hasVal().
2051 |
2052 | ```javascript
2053 | // bad
2054 | if (!dragon.age()) {
2055 | return false;
2056 | }
2057 |
2058 | // good
2059 | if (!dragon.hasAge()) {
2060 | return false;
2061 | }
2062 | ```
2063 |
2064 | - [23.4](#23.4) It's okay to create get() and set() functions, but be consistent.
2065 |
2066 | ```javascript
2067 | class Jedi {
2068 |
2069 | constructor(options = {}) {
2070 |
2071 | const lightsaber = options.lightsaber || 'blue';
2072 | this.set('lightsaber', lightsaber);
2073 |
2074 | }
2075 |
2076 | set(key, val) {
2077 |
2078 | this[key] = val;
2079 |
2080 | }
2081 |
2082 | get(key) {
2083 |
2084 | return this[key];
2085 |
2086 | }
2087 |
2088 | }
2089 | ```
2090 |
2091 | **[⬆ back to top](#table-of-contents)**
2092 |
2093 |
2094 | ## Events
2095 |
2096 | - [24.1](#24.1) When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass a hash instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event. For example, instead of:
2097 |
2098 | ```javascript
2099 | // bad
2100 | $(this).trigger('listingUpdated', listing.id);
2101 |
2102 | ...
2103 |
2104 | $(this).on('listingUpdated', function(e, listingId) {
2105 | // do something with listingId
2106 | });
2107 | ```
2108 |
2109 | prefer:
2110 |
2111 | ```javascript
2112 | // good
2113 | $(this).trigger('listingUpdated', { listingId : listing.id });
2114 |
2115 | ...
2116 |
2117 | $(this).on('listingUpdated', function(e, data) {
2118 | // do something with data.listingId
2119 | });
2120 | ```
2121 |
2122 | **[⬆ back to top](#table-of-contents)**
2123 |
2124 |
2125 | ## jQuery
2126 |
2127 | - [25.1](#25.1) Prefix jQuery object variables with a `$`.
2128 |
2129 | ```javascript
2130 | // bad
2131 | const sidebar = $('.sidebar');
2132 |
2133 | // good
2134 | const $sidebar = $('.sidebar');
2135 | ```
2136 |
2137 | - [25.2](#25.2) Cache jQuery lookups.
2138 |
2139 | ```javascript
2140 | // bad
2141 | function setSidebar() {
2142 |
2143 | $('.sidebar').hide();
2144 |
2145 | // ...stuff...
2146 |
2147 | $('.sidebar').css({
2148 | 'background-color': 'pink'
2149 | });
2150 |
2151 | }
2152 |
2153 | // good
2154 | function setSidebar() {
2155 |
2156 | const $sidebar = $('.sidebar');
2157 | $sidebar.hide();
2158 |
2159 | // ...stuff...
2160 |
2161 | $sidebar.css({
2162 | 'background-color': 'pink'
2163 | });
2164 |
2165 | }
2166 | ```
2167 |
2168 | - [25.3](#25.3) For DOM queries use Cascading `$('.sidebar ul')` or parent > child `$('.sidebar > ul')`. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16)
2169 | - [25.4](#25.4) Use `find` with scoped jQuery object queries.
2170 |
2171 | ```javascript
2172 | // bad
2173 | $('ul', '.sidebar').hide();
2174 |
2175 | // bad
2176 | $('.sidebar').find('ul').hide();
2177 |
2178 | // good
2179 | $('.sidebar ul').hide();
2180 |
2181 | // good
2182 | $('.sidebar > ul').hide();
2183 |
2184 | // good
2185 | $sidebar.find('ul').hide();
2186 | ```
2187 |
2188 | **[⬆ back to top](#table-of-contents)**
2189 |
2190 |
2191 | ## Type Annotations
2192 |
2193 |
2194 | - [26.1](#26.1) Type annotations placeholder.
2195 |
2196 |
2197 | - [26.2](#26.2) Use "T" for the type variable if only one is needed.
2198 |
2199 | ```javascript
2200 | function identify(arg: T): T {
2201 |
2202 | return arg;
2203 |
2204 | }
2205 | ```
2206 |
2207 | - [26.3](#26.3) If more than one type variable is required, start with letter "T" and name your variable in alphabetical sequence.
2208 |
2209 | ```javascript
2210 | function find(needle: T, haystack: U): U {
2211 |
2212 | return haystack.find(needle)
2213 |
2214 | }
2215 | ```
2216 |
2217 | - [26.4](#26.4) When possible, allow the compiler to infer type of variables.
2218 |
2219 | ```javascript
2220 | // bad
2221 | const output = identify("myString");
2222 |
2223 | // good
2224 | const output = identity("myString");
2225 | ```
2226 |
2227 | - [26.5](#26.5) When creating factories using generics, be sure to include the constructor function in the type.
2228 |
2229 | ```javascript
2230 | function create(thing: {new(): T;}): T {
2231 |
2232 | return new thing();
2233 |
2234 | }
2235 | ```
2236 |
2237 | **[⬆ back to top](#table-of-contents)**
2238 |
2239 |
2240 | ## Interfaces
2241 |
2242 |
2243 | - [27.1](#27.1) Interface placeholder.
2244 |
2245 | **[⬆ back to top](#table-of-contents)**
2246 |
2247 |
2248 | ## Organization
2249 |
2250 |
2251 | - [28.1](#28.1) 1 file per logical component, and each file should be divided into logical divisions via modules.
2252 |
2253 | ```javascript
2254 | module Automobile {
2255 |
2256 | module Honda {
2257 |
2258 | }
2259 |
2260 | }
2261 | ```
2262 |
2263 | - [28.2](#28.2) Export one main module per file so it can be required by other files.
2264 |
2265 | ```javascript
2266 | module Automobile {
2267 |
2268 | // hidden module, will not be accessible via "require"
2269 | Honda {
2270 |
2271 | }
2272 |
2273 | // public module, will be accessible via "require"
2274 | export Ford {
2275 |
2276 | export function vroom() {
2277 |
2278 | console.log('vroom!');
2279 |
2280 | }
2281 |
2282 | }
2283 |
2284 | }
2285 |
2286 | export default Automobile;
2287 | ```
2288 |
2289 | - [28.3](#28.3) Order your code (alphabetically) in the following order within each module:
2290 | - var
2291 | - export var
2292 | - let
2293 | - export let
2294 | - const
2295 | - export const
2296 | - interface
2297 | - export interface
2298 | - function
2299 | - export function
2300 | - class
2301 | - export class
2302 | - module
2303 | - export module
2304 |
2305 | **[⬆ back to top](#table-of-contents)**
2306 |
2307 |
2308 | ## ECMAScript 5 Compatibility
2309 |
2310 | - [29.1](#29.1) Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/).
2311 |
2312 | **[⬆ back to top](#table-of-contents)**
2313 |
2314 | ## ECMAScript 6 Styles
2315 |
2316 | - [30.1](#30.1) This is a collection of links to the various es6 features.
2317 |
2318 | 1. [Arrow Functions](#arrow-functions)
2319 | 1. [Classes](#constructors)
2320 | 1. [Object Shorthand](#es6-object-shorthand)
2321 | 1. [Object Concise](#es6-object-concise)
2322 | 1. [Object Computed Properties](#es6-computed-properties)
2323 | 1. [Template Strings](#es6-template-literals)
2324 | 1. [Destructuring](#destructuring)
2325 | 1. [Default Parameters](#es6-default-parameters)
2326 | 1. [Rest](#es6-rest)
2327 | 1. [Array Spreads](#es6-array-spreads)
2328 | 1. [Let and Const](#references)
2329 | 1. [Iterators and Generators](#iterators-and-generators)
2330 | 1. [Modules](#modules)
2331 |
2332 | **[⬆ back to top](#table-of-contents)**
2333 |
2334 | ## Typescript 1.5 Styles
2335 |
2336 | - [31.1](#31.1) This is a collection of links to the various es6 features.
2337 |
2338 | 1. [Type Annotations](#ts-type-annotations)
2339 | 1. [Interfaces](#ts-interfaces)
2340 | 1. [Classes](#ts-classes)
2341 | 1. [Modules](#ts-modules)
2342 | 1. [Generics](#ts-generics)
2343 |
2344 | **[⬆ back to top](#table-of-contents)**
2345 |
2346 | ## License
2347 |
2348 | (The MIT License)
2349 |
2350 | Copyright (c) 2014 Airbnb
2351 |
2352 | Permission is hereby granted, free of charge, to any person obtaining
2353 | a copy of this software and associated documentation files (the
2354 | 'Software'), to deal in the Software without restriction, including
2355 | without limitation the rights to use, copy, modify, merge, publish,
2356 | distribute, sublicense, and/or sell copies of the Software, and to
2357 | permit persons to whom the Software is furnished to do so, subject to
2358 | the following conditions:
2359 |
2360 | The above copyright notice and this permission notice shall be
2361 | included in all copies or substantial portions of the Software.
2362 |
2363 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
2364 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2365 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
2366 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
2367 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2368 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2369 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2370 |
2371 | **[⬆ back to top](#table-of-contents)**
2372 |
--------------------------------------------------------------------------------
/linters/README.md:
--------------------------------------------------------------------------------
1 | ## `tslint.json`
2 |
3 | Our `tslint.json` requires the following NPM packages packages:
4 |
5 | - `tslint`
6 |
--------------------------------------------------------------------------------
/linters/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "align": [
4 | true,
5 | "parameters",
6 | "arguments",
7 | "statements"
8 | ],
9 | "ban": false,
10 | "class-name": true,
11 | "comment-format": [
12 | true,
13 | "check-space",
14 | "check-lowercase"
15 | ],
16 | "curly": false,
17 | "eofline": true,
18 | "forin": true,
19 | "indent": [
20 | true,
21 | 2
22 | ],
23 | "interface-name": false,
24 | "jsdoc-format": true,
25 | "label-position": true,
26 | "label-undefined": true,
27 | "max-line-length": [
28 | true,
29 | 140
30 | ],
31 | "member-ordering": [
32 | true,
33 | "public-before-private",
34 | "static-before-instance",
35 | "variables-before-functions"
36 | ],
37 | "no-any": false,
38 | "no-arg": true,
39 | "no-bitwise": true,
40 | "no-console": [
41 | true,
42 | "debug",
43 | "info",
44 | "time",
45 | "timeEnd",
46 | "trace"
47 | ],
48 | "no-construct": true,
49 | "no-constructor-vars": true,
50 | "no-debugger": true,
51 | "no-duplicate-key": true,
52 | "no-shadowed-variable": true,
53 | "no-duplicate-variable": true,
54 | "no-empty": true,
55 | "no-eval": true,
56 | "no-require-imports": true,
57 | "no-string-literal": true,
58 | "no-switch-case-fall-through": true,
59 | "trailing-comma": {
60 | "singleline": "never",
61 | "multiline": "always"
62 | },
63 | "no-trailing-whitespace": true,
64 | "no-unreachable": true,
65 | "no-unused-expression": true,
66 | "no-unused-variable": true,
67 | "no-use-before-declare": true,
68 | "no-var-keyword": true,
69 | "no-var-requires": false,
70 | "one-line": [
71 | true,
72 | "check-open-brace",
73 | "check-catch",
74 | "check-else",
75 | "check-whitespace"
76 | ],
77 | "quotemark": [
78 | true,
79 | "single"
80 | ],
81 | "radix": true,
82 | "semicolon": true,
83 | "switch-default": true,
84 | "triple-equals": [
85 | true,
86 | "allow-null-check"
87 | ],
88 | "typedef": [
89 | true,
90 | "call-signature",
91 | "parameter",
92 | "property-declaration",
93 | "member-variable-declaration"
94 | ],
95 | "typedef-whitespace": [
96 | true,
97 | {
98 | "call-signature": "nospace",
99 | "index-signature": "nospace",
100 | "parameter": "nospace",
101 | "property-declaration": "nospace",
102 | "variable-declaration": "nospace"
103 | }
104 | ],
105 | "use-strict": [
106 | false,
107 | "check-module",
108 | "check-function"
109 | ],
110 | "variable-name": false,
111 | "whitespace": [
112 | true,
113 | "check-branch",
114 | "check-decl",
115 | "check-operator",
116 | "check-separator",
117 | "check-type"
118 | ]
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "airbnb-style",
3 | "version": "2.0.0",
4 | "description": "A mostly reasonable approach to JavaScript.",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1",
7 | "publish-all": "npm publish && cd ./packages/eslint-config-airbnb && npm publish"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/airbnb/javascript.git"
12 | },
13 | "keywords": [
14 | "style guide",
15 | "lint",
16 | "airbnb",
17 | "es6",
18 | "es2015",
19 | "react",
20 | "jsx"
21 | ],
22 | "author": "Harrison Shoff (https://twitter.com/hshoff)",
23 | "license": "MIT",
24 | "bugs": {
25 | "url": "https://github.com/airbnb/javascript/issues"
26 | },
27 | "homepage": "https://github.com/airbnb/javascript"
28 | }
29 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/index.js:
--------------------------------------------------------------------------------
1 | var resolve = require('resolve');
2 | var stripComments = require('strip-json-comments');
3 | var fs = require('fs');
4 |
5 | // you could do this all at once if you wanted to look cool
6 | var filename = resolve.sync('airbnb-style/linters/.eslintrc');
7 | var data = fs.readFileSync(filename, {encoding: 'utf-8'});
8 | var dataWithoutComments = stripComments(data);
9 | var parsed = JSON.parse(dataWithoutComments);
10 |
11 | module.exports = parsed;
12 |
--------------------------------------------------------------------------------
/packages/eslint-config-airbnb/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eslint-config-airbnb",
3 | "version": "0.0.6",
4 | "description": "Airbnb's ESLint config, following our styleguide",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/airbnb/javascript"
12 | },
13 | "keywords": [
14 | "eslint",
15 | "eslintconfig",
16 | "config",
17 | "airbnb",
18 | "javascript",
19 | "styleguide"
20 | ],
21 | "author": "Jake Teton-Landis (https://twitter.com/@jitl)",
22 | "license": "MIT",
23 | "bugs": {
24 | "url": "https://github.com/airbnb/javascript/issues"
25 | },
26 | "homepage": "https://github.com/airbnb/javascript",
27 | "dependencies": {
28 | "airbnb-style": "2.0.0",
29 | "babel-eslint": "3.1.7",
30 | "eslint": "0.21.2",
31 | "eslint-plugin-react": "2.3.0",
32 | "resolve": "1.1.6",
33 | "strip-json-comments": "1.0.2"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------