Clean and Typechecked JS
Arthur Reis Puthin
About me
Developer at ilegra (think beyond!)
Mostly front-end stuff
Undergraduate at Unisinos
/aputhin
Para enviar uma pergunta e receber
o material desta apresentação acesse:
bit.ly/arthurtdc
JS is nuts
Let's fix it where we
are best at: the code
"The most often-cited rationales for
type systems are that they:
1. Catch errors early
2. Improve readability of code
3. Facilitate tooling
4. Improve runtime performance"
http://www.hammerlab.org/2015/12/09/our-experiences-with-flow/
"The results are encouraging; we
found that using Flow or TypeScript
could have prevented 15% of the
public bugs for public projects on
GitHub."
http://ttendency.cs.ucl.ac.uk/projects/type_study/
Pick your poison:
➔ Supersets (i.e. TypeScript or JavaScript++)
➔ Static Typecheckers (i.e. Flow)
➔ Compile to JS (i.e. Elm or PureScript)
Meet Flow.
https://flow.org/
How does it work?
// @flow
function sqr(n: number): number {
return n * n;
}
sqr("2"); // Error!
// setup babel to clean it up
// install the binary
npm install --save-dev flow-bin
// add it to package.json
"scripts": {"flow": "flow"}
// run to check for errors
npm run flow init
npm run flow
npm run flow stop
Type Annotation Basics
let isOpen: boolean = true;
const answer: number = 42; // includes Infinity and NaN
let company: string = 'ilegra';
const hope: null = null;
let theQuestion: void = undefined; // uncompatible with null!
const words: Array<string> = ['I', 'am', 'groot']; // Array<T>
let iCanBeAnything: any = 'es' + 2016; // wildcard, DON'T USE IT
// "Using any is completely unsafe, and should be avoided whenever
possible" - https://flow.org/en/docs/types/any/
Object & Function Typing
let proplessObject: Object = {}; // works, but loses typechecking goodness
const identity: { name: string, age: number } = {
name: 'Arthur',
age: 26,
};
let booksPageCounts: { [name: string]: number } = {
Javascript: 999,
JSGoodParts: 100,
};
const calculateArea = (radius: number): number => { // return type is optional
return 3.14 * radius * radius;
};
Type Alias
type Address = {
street: string,
number: number
};
let myWorkAdress: Address = {
street: 'Washington Luiz',
number: 820,
};
type Email = string; // aliasing
var myEmail: Email = 'arthur.rprp@gmail.com';
Generics, Maybe and Optionals
type Password<T> = { key: T };
let numberKey: Password<number> = { key: 12345678 };
let stringKey: Password<string> = { key: "shhhhh" };
let arrayKey: Password<Array<number>> = { key: [4, 2] }
var message: ?string = null; // accepts string, null or undefined
function acceptsOptString(value?: string) {} // accepts string or undefined
Literals & Union
function getColor(name: "success" | "warning" | "danger") {
// ...
}
getColor("success"); // Works!
getColor("danger"); // Works!
getColor("error"); // Error!
// accepts any number, boolean or string
function toStringPrimitives(value: number | boolean | string) {
return String(value);
}
Meet Flow.
https://flow.org/
Types: ✓
But we have to go further...
“It is not the language that makes
programs appear simple. It is the
programmer that make the language
appear simple!”
Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship
Keep an eye for
code smells...
Code Smells:
Non-strict Equality
// All of these evaluate to 'true'
console.log(false == '0');
console.log(null == undefined);
console.log(" trn" == 0);
console.log('' == 0);
console.log(0 == '0');
if ({}) {...}
if ([]) {...}
// Always go for strict equality/inequality
console.log(0 === '0'); // false
// random gotcha: NaN comparison
console.log(NaN == NaN); // false
console.log(NaN === NaN); // false
console.log(isNaN(NaN)); // true, but beware
console.log(NaN != NaN); // true
Code Smells:
Incorrect context
Game.prototype.restart = function () {
this.clearLocalStorage();
this.timer = setTimeout(function() {
this.clearBoard(); // what is "this"?
}, 0);
};
// Solution 1: explicit binding with .bind()
let clearInTime = function() {
this.clearBoard();
};
this.clearInTime = clearInTime.bind(this);
// Solution 2: arrow functions
this.clearInTime = setTimeout(() => {
this.clearBoard();
}, 0);
Code Smells:
Variable scoping
and handling
for (var i = 0; i < 10; i++) {
elements[i].onclick = function() {
console.log("This is element #" + i); // 10
};
}
console.log(i); // = 10; i "leaks" out of scope
// solution: let (ES6) is block-level scoped
function () {
var a = 1;
let b = 2;
}
console.log(a); // 1
console.log(b); // undefined
/* const is NOT for scope hoisting: it is for
read-only/immutable values! */
Code Smells:
Anonymous
Functions
// debugging this stack trace might go awry...
document.querySelector('button')
.addEventListener('input', function(e) {
// ...
});
// naming favors stack-tracing and code reuse
const kaboom = function() { alert('Ka-boom') };
document.querySelector('button')
.addEventListener('click', kaboom);
document.querySelector('#egg')
.addEventListener('mouseenter', kaboom);
More Anti-patterns to keep in mind...
- Too many global namespace variables
- Too much direct DOM manipulation
- Modifying the Object class prototype (or any prototype for that matter…)
JS Clean Code:
Fewer Arguments
// BAD
function createMenu(title, body, buttonText,
cancellable) {...}
// GOOD
function createMenu({ title, body, buttonText,
cancellable }) {...}
createMenu({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
});
src: github.com/ryanmcdermott/clean-code-javascript
JS Clean Code:
Avoid Side Effects
src: github.com/ryanmcdermott/clean-code-javascript
function splitIntoFirstAndLastName(name) {
return name.split(' ');
}
const name = 'Ryan McDermott';
const newName =
splitIntoFirstAndLastName(name);
console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott'];
// Special care when working with arrays/obj
const addItemToCart = (cart, item) => {
return [...cart, { item, date: Date.now() }];
};
JS Clean Code:
Favor Functional
src: github.com/ryanmcdermott/clean-code-javascript
const programmers = [
{name: 'Uncle Bobby', linesOfCode: 500},
{name: 'Gracie Hopper', linesOfCode: 1000}];
// BAD
let totalOutput = 0;
for (let i = 0; i < programmerOutput.length;
i++) {
totalOutput += programmers[i].linesOfCode;
}
// GOOD
const totalOutput = programmers
.map((programmer) => programmer.linesOfCode)
.reduce((acc, linesOfCode) => acc +
linesOfCode, INITIAL_VALUE);
JS Clean Code:
Promises over CBs
src: github.com/ryanmcdermott/clean-code-javascript
// BAD
get(LINK, (requestErr, response) => {
if (requestErr) {...}
else {
writeFile('article.html', response.body, (writeErr) => {
if (writeErr) {...}
else {...}
});
}
});
// GOOD
get(LINK)
.then((response) => writeFile('article.html', response))
.then(() => {...})
.catch((err) => {...});
// BEST
async function getCleanCodeArticle() {
try {
const response = await get(LINK);
await writeFile('article.html', response);
} catch(err) {...}
}
"Code formatting is about communication,
and communication is the professional
developer’s first order of business."
Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship
Above all:
consistency
Thanks for your time!
Hope you enjoyed :)
Questions? bit.ly/arthurtdc
/aputhin

Clean & Typechecked JS

  • 1.
    Clean and TypecheckedJS Arthur Reis Puthin
  • 2.
    About me Developer atilegra (think beyond!) Mostly front-end stuff Undergraduate at Unisinos /aputhin
  • 3.
    Para enviar umapergunta e receber o material desta apresentação acesse: bit.ly/arthurtdc
  • 9.
  • 10.
    Let's fix itwhere we are best at: the code
  • 11.
    "The most often-citedrationales for type systems are that they: 1. Catch errors early 2. Improve readability of code 3. Facilitate tooling 4. Improve runtime performance" http://www.hammerlab.org/2015/12/09/our-experiences-with-flow/
  • 12.
    "The results areencouraging; we found that using Flow or TypeScript could have prevented 15% of the public bugs for public projects on GitHub." http://ttendency.cs.ucl.ac.uk/projects/type_study/
  • 14.
    Pick your poison: ➔Supersets (i.e. TypeScript or JavaScript++) ➔ Static Typecheckers (i.e. Flow) ➔ Compile to JS (i.e. Elm or PureScript)
  • 15.
  • 16.
    How does itwork? // @flow function sqr(n: number): number { return n * n; } sqr("2"); // Error! // setup babel to clean it up // install the binary npm install --save-dev flow-bin // add it to package.json "scripts": {"flow": "flow"} // run to check for errors npm run flow init npm run flow npm run flow stop
  • 17.
    Type Annotation Basics letisOpen: boolean = true; const answer: number = 42; // includes Infinity and NaN let company: string = 'ilegra'; const hope: null = null; let theQuestion: void = undefined; // uncompatible with null! const words: Array<string> = ['I', 'am', 'groot']; // Array<T> let iCanBeAnything: any = 'es' + 2016; // wildcard, DON'T USE IT // "Using any is completely unsafe, and should be avoided whenever possible" - https://flow.org/en/docs/types/any/
  • 18.
    Object & FunctionTyping let proplessObject: Object = {}; // works, but loses typechecking goodness const identity: { name: string, age: number } = { name: 'Arthur', age: 26, }; let booksPageCounts: { [name: string]: number } = { Javascript: 999, JSGoodParts: 100, }; const calculateArea = (radius: number): number => { // return type is optional return 3.14 * radius * radius; };
  • 19.
    Type Alias type Address= { street: string, number: number }; let myWorkAdress: Address = { street: 'Washington Luiz', number: 820, }; type Email = string; // aliasing var myEmail: Email = 'arthur.rprp@gmail.com';
  • 20.
    Generics, Maybe andOptionals type Password<T> = { key: T }; let numberKey: Password<number> = { key: 12345678 }; let stringKey: Password<string> = { key: "shhhhh" }; let arrayKey: Password<Array<number>> = { key: [4, 2] } var message: ?string = null; // accepts string, null or undefined function acceptsOptString(value?: string) {} // accepts string or undefined
  • 21.
    Literals & Union functiongetColor(name: "success" | "warning" | "danger") { // ... } getColor("success"); // Works! getColor("danger"); // Works! getColor("error"); // Error! // accepts any number, boolean or string function toStringPrimitives(value: number | boolean | string) { return String(value); }
  • 22.
  • 23.
    Types: ✓ But wehave to go further...
  • 24.
    “It is notthe language that makes programs appear simple. It is the programmer that make the language appear simple!” Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship
  • 25.
    Keep an eyefor code smells...
  • 26.
    Code Smells: Non-strict Equality //All of these evaluate to 'true' console.log(false == '0'); console.log(null == undefined); console.log(" trn" == 0); console.log('' == 0); console.log(0 == '0'); if ({}) {...} if ([]) {...} // Always go for strict equality/inequality console.log(0 === '0'); // false // random gotcha: NaN comparison console.log(NaN == NaN); // false console.log(NaN === NaN); // false console.log(isNaN(NaN)); // true, but beware console.log(NaN != NaN); // true
  • 27.
    Code Smells: Incorrect context Game.prototype.restart= function () { this.clearLocalStorage(); this.timer = setTimeout(function() { this.clearBoard(); // what is "this"? }, 0); }; // Solution 1: explicit binding with .bind() let clearInTime = function() { this.clearBoard(); }; this.clearInTime = clearInTime.bind(this); // Solution 2: arrow functions this.clearInTime = setTimeout(() => { this.clearBoard(); }, 0);
  • 28.
    Code Smells: Variable scoping andhandling for (var i = 0; i < 10; i++) { elements[i].onclick = function() { console.log("This is element #" + i); // 10 }; } console.log(i); // = 10; i "leaks" out of scope // solution: let (ES6) is block-level scoped function () { var a = 1; let b = 2; } console.log(a); // 1 console.log(b); // undefined /* const is NOT for scope hoisting: it is for read-only/immutable values! */
  • 29.
    Code Smells: Anonymous Functions // debuggingthis stack trace might go awry... document.querySelector('button') .addEventListener('input', function(e) { // ... }); // naming favors stack-tracing and code reuse const kaboom = function() { alert('Ka-boom') }; document.querySelector('button') .addEventListener('click', kaboom); document.querySelector('#egg') .addEventListener('mouseenter', kaboom);
  • 30.
    More Anti-patterns tokeep in mind... - Too many global namespace variables - Too much direct DOM manipulation - Modifying the Object class prototype (or any prototype for that matter…)
  • 31.
    JS Clean Code: FewerArguments // BAD function createMenu(title, body, buttonText, cancellable) {...} // GOOD function createMenu({ title, body, buttonText, cancellable }) {...} createMenu({ title: 'Foo', body: 'Bar', buttonText: 'Baz', cancellable: true }); src: github.com/ryanmcdermott/clean-code-javascript
  • 32.
    JS Clean Code: AvoidSide Effects src: github.com/ryanmcdermott/clean-code-javascript function splitIntoFirstAndLastName(name) { return name.split(' '); } const name = 'Ryan McDermott'; const newName = splitIntoFirstAndLastName(name); console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; // Special care when working with arrays/obj const addItemToCart = (cart, item) => { return [...cart, { item, date: Date.now() }]; };
  • 33.
    JS Clean Code: FavorFunctional src: github.com/ryanmcdermott/clean-code-javascript const programmers = [ {name: 'Uncle Bobby', linesOfCode: 500}, {name: 'Gracie Hopper', linesOfCode: 1000}]; // BAD let totalOutput = 0; for (let i = 0; i < programmerOutput.length; i++) { totalOutput += programmers[i].linesOfCode; } // GOOD const totalOutput = programmers .map((programmer) => programmer.linesOfCode) .reduce((acc, linesOfCode) => acc + linesOfCode, INITIAL_VALUE);
  • 34.
    JS Clean Code: Promisesover CBs src: github.com/ryanmcdermott/clean-code-javascript // BAD get(LINK, (requestErr, response) => { if (requestErr) {...} else { writeFile('article.html', response.body, (writeErr) => { if (writeErr) {...} else {...} }); } }); // GOOD get(LINK) .then((response) => writeFile('article.html', response)) .then(() => {...}) .catch((err) => {...}); // BEST async function getCleanCodeArticle() { try { const response = await get(LINK); await writeFile('article.html', response); } catch(err) {...} }
  • 35.
    "Code formatting isabout communication, and communication is the professional developer’s first order of business." Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship
  • 36.
  • 38.
    Thanks for yourtime! Hope you enjoyed :) Questions? bit.ly/arthurtdc /aputhin