Static type checking & JavaScript
by @andreypopp
Reasons for static type checking?
 
 
 
undefined is not a function
Reasons for static type checking?
1. Less bugs
 
 
Reasons for static type checking?
1. Less bugs
2. Safe refactorings
 
    // types are X-RAY for code

    function getUser(
      id: number, <-- first arg is a number
      options: {skipCache: boolean} <-- second arg is an object
    ): Promise<?User> { <-- returns a promise with either null or User
      ...
    }
  
Reasons for static type checking?
1. Less bugs
2. Safe refactorings
3. Easier to maintain
    let incomeByDay = [
      {income: 12},
      {income: 13}
    ]

    incomeByDay.push({incme: 15})

    let sum: number = incomeByDay.reduce(
      (a, b) => a + b.income,
      0
    )
  
    let incomeByDay = [
      {income: 12},
      {income: 13}
    ]

    incomeByDay.push({incme: 15})

    let sum: number = incomeByDay.reduce(
      (a, b) => a + b.income,
      0
    )
  
TypeScript
FlowType
Closure Compiler
...
    type User = {
      name: string;
      age: number;
    }

    let u: User = {name: 'John', age: 25}

    function addition(a: number, b: number): number {
      return a + b
    }
  
TypeScript
written in TypeScript
compiles ES2015 to ES5*
no need for babel*
more syntax
feels like a language
FlowType
written in OCaml
just typechecks
you need babel
compat with JS tools
Type Systems
DX (Type inference, tooling, ...)
Soundness (correctness, ...)
Ecosystem
    // Type inference in TypeScript


    export function addition(a: number, b: number) {
      return doAddition(a, b)
    }

    // can't infer argument types and complain :(
    function doAddition(a, b) {
      return a + b
    }
  
    // Type inference in FlowType

    // need to annotate only exports
    export function addition(a: number, b: number) {
      return doAddition(a, b)
    }

    // infers argument and return types!
    function doAddition(a: number, b: number): number {
      return a + b
    }
  
    // "Soundness" in TypeScript

    let promisedString: Promise<string> = new Promise(
      resolve => {
        // Perfectly valid in TypeScript!
        resolve(42)
      }
    )
  
Flow is about control flow analysis (CFA)
    // define.js

    function fetchCurrentUser() {
      return hasCurrentUser ? {name: 'John'} : null
    }

    // use.js

    let user = fetchCurrentUser()

    console.log(user.name)

    if (user != null) {
      // hm... we checked for != null so that must be safe to use "user"
      console.log(user.name)
    }
  
    // Flow: objects

    type User = {
      name: string;
      age: number;
    }

    let user: User = {
      name: 'John',
      age: 25,
      phone: '555-55-55'
    }

  
    // Flow: unions

    type MaybeLazyNumber =
      | number
      | () => number

    function getNumber(n: MaybeLazyNumber): number {
      if (typeof n === 'function') {
        return n()
      } else {
        return n
      }
    }

    getNumber(42)
    getNumber(() => computeFactorial(42))
    getNumber('oops')
  
    // Flow: intersections

    type Person = {
      name: string;
    }

    type HasCat = {
      theirCatName: string;
    }

    function hello(
      person: Person & HasCat
    ) {
      console.log(`${person.name} has cat named ${person.theirCatName}`)
    }
  
    // Flow: exact object types

    type User = {| name: string; age: number; |}

    let john: User = {name: 'John', age: 25}

    john.phone = '555-55-55'
  
    // Flow: mixed type

    let user: mixed = JSON.parse('{"name": "John"}')

    console.log(user.name)

    if (
      typeof user === 'object' &&
      user != null &&
      user.name != null
    ) {
      console.log(user.name)
    }
  
    // Flow: validating external data

    import {object, string} from 'validated/schema'
    import {validate} from 'validated/object'

    let schema = object({name: string})

    let data: mixed = JSON.parse('{"name": "John"}')
    let user: {name: string} = validate(schema, data)

    console.log(user.name)
    console.log(user.age)
  
    // Flow: literal types (modeling enums)

    type DownloadState = 
      | 'INIT'
      | 'IN-PROGRESS'
      | 'FINISHED'
      | 'ERRORED'

    let state: DownloadState = 'INPROGRESS'
  
    // Flow "hacks": refinements

    class IsInteger {}

    export type Integer = number & IsInteger;

    export function integer(a: number): ?Integer {
      return a % 1 === 0 ? ((a: any): Integer) : null
    }

    // more at http://tinyurl.com/flow-refinements
  
    // Flow "hacks": phantom types

    class Validated {}
    class Unvalidated {}

    class Data<State> {
      value: string
      constructor(value: string) {
        this.value = value
      }
    }

  
    // Flow "hacks": phantom types (pt 2)

    export function make(input: string): Data<Unvalidated> {
      return new Data(input)
    }

    export function validate(data: Data<Unvalidated>): ?Data<Validated> {
      if (!isValid(data)) {
        throw new Error('data is not valid')
      }
      return (data: any)
    }

    function use(data: Data<Validated>): void {
      console.log(‘using ‘ + data.value)
    }

    // more at http://tinyurl.com/flow-phantom-types
  
    // Flow "hacks": immutability

    type User = {
      +name: string;
    }

    let u: User = {name: 'John'}

    console.log(u.name)

    u.name = 'Jack'

    // more at http://tinyurl.com/flow-immut
  
Also:
flow-typed
eslint-plugin-flowtype-errors
facebookincubator/create-react-app#1152
flow-coverage-report
What's next?
OCaml / Reason + BuckleScript
PureScript
Ur / Web
Idris
Haskell + GHCJS
F# + Fable
Elm
Questions?
@andreypopp
github.com/andreypopp