Presented at EmpireJS on April 26, 2015. This talk introduces static type checking, and examines the multiple type checking tools available in the JavaScript ecosystem today. It also goes through the steps necessary to add gradual type checking to a legacy codebase, and examines the benefits for doing so.
3. Change all the types...
@AndrewRota | #empirejs
var x; // x starts undefined
typeof x === 'undefined'
x = 5; // now it's is a number
typeof x === 'number'
x = 'five'; // now it's a string
typeof x === 'string'
x = true; // now it's a boolean
typeof x === 'boolean'
x = {value: 'five'}; // now it's an object
typeof x === 'object'
JS
3/48
4. We have types, but they're not static.
(No type is known or declared at compile time)
@AndrewRota | #empirejs 4/48
5. 7 Types in JavaScript
@AndrewRota | #empirejs
Boolean Number String Object
Symbol Null Undefined
5/48
6. Most code has type expectations
...we just don't always acknowledge it.
@AndrewRota | #empirejs
function add(arr) {
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
add([2,3]); // returns 5 as expected
add(['foo', 'bar']); // returns "0foobar" (expected?)
add(null); // Throws exception: cannot read prop 'length' of null
JS
6/48
8. Ad Hoc Runtime Checks
@AndrewRota | #empirejs
function add(arr) {
if (!Array.isArray(arr)) return;
var sum = 0;
for (var i = 0; i < arr.length; i++) {
if (typeof arr[i] !== 'number') return;
sum += arr[i];
}
return sum;
}
JS
8/48
9. Ad Hoc Runtime Checks
@AndrewRota | #empirejs
function add(arr) {
if (!Array.isArray(arr)) throw('not an array');
var sum = 0;
for (var i = 0; i < arr.length; i++) {
if (typeof arr[i] !== 'number') throw('not a number');
sum += arr[i];
}
return sum;
}
JS
9/48
10. Type Annotations
@AndrewRota | #empirejs
/**
* Add all numbers in an array
*
* @param {number[]}
* @return {number}
*/
function add(arr) {
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
JS
10/48
11. Type Annotations
Explicit typing...without any checks.
@AndrewRota | #empirejs
/**
* Add all numbers in an array
*
* @param {number[]}
* @return {number}
*/
function add(arr) {
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
JS
11/48
14. ActionScript
Early 2000s | Partially conformed to ECMAScript 4
@AndrewRota | #empirejs
private function add(arr:Array):int{
var sum:int = 0;
for (var i:int = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
AS
14/48
15. Closure Compiler
~2009
@AndrewRota | #empirejs
/**
* Add all numbers in an array
*
* @param {number[]}
* @return {number}
*/
function add(arr) {
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
JS
15/48
16. TypeScript
2012 | Compiles to JavaScript, optional static typing
@AndrewRota | #empirejs
function add(arr:Array<number>):number{
var sum:number = 0;
for (var i:number = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
TS
16/48
17. Flow
2014 | Compiles to JavaScript, optional static typing
@AndrewRota | #empirejs
/* @flow */
function add(arr:Array<number>):number{
var sum:number = 0;
for (var i:number = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
FLOW
17/48
18. Flow Comments
2015 | Alternative to a transpile step
@AndrewRota | #empirejs
/* @flow */
function add(arr/*:Array<number>*/)/*:number*/{
var sum/*:number*/ = 0;
for (var i/*:number*/ = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
JS
18/48
19. Released Runtime Env. No Transpile Null Checking ES6
Closure Compiler 2009 Java ✓ X Some
TypeScript 2012 JavaScript X X Some
Flow 2014 OCaml X ✓ Some
Flow Comments 2015 OCaml ✓ ✓ Some
@AndrewRota | #empirejs 19/48
21. Step 1: Choose a Type Checker
Step 2: Set Up a Transpile Step
Step 3: Add Type Annotations
@AndrewRota | #empirejs 21/48
22. Step 1: Choose a Type Checker
Step 2: Set Up a Transpile Step
Step 3: Add Type Annotations
@AndrewRota | #empirejs 22/48
23. TypeScript Flow
TypeScript vs. Flow
@AndrewRota | #empirejs
Released 2012
Written in JS: any OS
Community-provided
declaration files
Addtional transpiled features
(defaults, overloads)
·
·
·
·
Released 2014
Written in OCaml: OSX, Linux
Built-in null handling.
Comment-only syntax
available
·
·
·
·
23/48
24. Step 1: Choose a Type Checker
Step 2: Set Up a Transpile Step
Step 3: Add Type Annotations
@AndrewRota | #empirejs 24/48
25. Setting Up Flow
1. Install Flow from flowtype.org
2. Add transformer ( JSX or Babel ) to your build
@AndrewRota | #empirejs 25/48
26. Using Flow
1. Run flow check
2. Run build with transformer.
@AndrewRota | #empirejs
Check → Transform
26/48
31. Step 1: Choose a Type Checker
Step 2: Set Up a Transpile Step
Step 3: Add Type Annotations
@AndrewRota | #empirejs 31/48
32. Type Inference
Some of your work's already done!
Flow: property length: Property not found in Number
TypeScript: Property 'length' does not exist on type 'number'
@AndrewRota | #empirejs
var x = 1;
x.length;
JS
32/48
35. Union Types
ThisType | ThatType
@AndrewRota | #empirejs
var x: number | string = 0;
x = 'foo';
TS/FLOW
35/48
36. Null Checks
Flow has the advantage here
Flow: property x: Property cannot be accessed on possibly null value
TypeScript: no error
@AndrewRota | #empirejs
var x = null;
x.foo;
JS
36/48
37. Functions
Both arguments and return values (the function itself)
@AndrewRota | #empirejs
function helloWorld(name: string):string {
return 'Hello, ' + name;
}
function addExclamation(sentence: string):string {
return sentence + '!';
}
addExclamation(helloWorld('EmpireJS'));
TS/FLOW
37/48
41. But is it worth it?
@AndrewRota | #empirejs 41/48
42. Catch More Bugs at Compile Time
Flow: null: This type is incompatible with string
@AndrewRota | #empirejs
function helloWorld(name:string) {
return 'Hello, ' + name;
}
helloWorld('EmpireJS');
helloWorld(null);
FLOW
42/48
44. Easier to Reason About Code Flow
@AndrewRota | #empirejs
In: Number
Out: String
↗
In: String
Out: Object
↗
In: Object
Out: Boolean
44/48
45. - Bertrand Meyer
Correctness
“ The ability of software products to perform their exact tasks, as
defined by their specification.
@AndrewRota | #empirejs 45/48
47. - TypeScript and the Road to 2.0
And someday it might be in JavaScript...
“ The TypeScript team is [...] looking forward to working together going
forward and creating the best tools we can for the JavaScript community.
In the long term, we will also be working to fold the best features of
these tools into ECMAScript, the standard behind JavaScript.
@AndrewRota | #empirejs 47/48