├── .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 | --------------------------------------------------------------------------------