The Road to a Statically Typed Future (ReactAlicante 2017)

The Road to a Statically Typed Future (ReactAlicante 2017)

JavaScript's dynamic nature and rich and expressive syntax are strengths of the language, making it an approachable and beginner-friendly language to get started with. However, this same flexibility through dynamic typing and runtime evaluation increases the difficulty of debugging and makes it tough to confidently change existing code.

This talk will discuss Patrick's personal journey and experience on using existing type systems within and outside of the JavaScript ecosystem, specifically Flow and ReasonML, and demonstrate how they can improve your workflow.

Patrick will talk about general concepts of why we need type definitions, how types will influence API designs, the differences between static and runtime types and how we can bridge the gap between a statically compiled, type-safe language and the JavaScript ecosystem.

This presentation is based on Pt.1 of this presentation series... in Pt. 2 he will demo how types will translate from the Flow type checker to the ReasonML language by using a practical example.

Presented on:
- 30th of September 2017 (React Alicante 2017)

7dce9b480f93b02761519348359c4b71?s=128

Patrick Stapfer

September 30, 2017
Tweet

Transcript

  1. 3.
  2. 4.
  3. 6.
  4. 7.
  5. 8.
  6. 9.
  7. 10.
  8. 11.
  9. 12.
  10. 13.

    const toSiteHostStr = (obj) => R.compose( R.ifElse( R.compose( // (val)

    => val.length > 1, R.lt(1), R.length ), R.join('.'), R.compose(R.always('')) ), R.values, R.pick(['serverIdentifier', 'serverInfrastructure']) )(obj);
  11. 14.
  12. 15.
  13. 16.
  14. 17.
  15. 18.
  16. 19.
  17. 20.
  18. 21.
  19. 22.
  20. 23.
  21. 24.
  22. 25.
  23. 26.
  24. 27.
  25. 28.
  26. 29.
  27. 30.
  28. 31.
  29. 32.
  30. 33.

    type State = { board: Board, progress: Progress, }; type

    Token = "X" | "O" | "Empty"; type Board = Array<Token>; type Player = "X" | "O"; type Progress = 
 { type: "turn", player: Player } | { type: "win", player: Player } | { type: "draw" }
  31. 34.

    Types in Action switch (progress.type) { case "turn": const {player}

    = progress; return `Player ${player}'s turn`; case "win": const {player} = progress; return `Player ${player} won`; }; const progress: Progress = { type: "turn", player: "X" };
  32. 35.

    "Pattern Matching" in Flow switch (progress.type) { case "turn": const

    {player} = progress; return `Player ${player}'s turn`; case "winner": const {player} = progress; return `Player ${player} won`; }; switch is no expression progress = typeof Object flow infer is brittle typoooooos No checks for exhaustiveness verbose syntax
  33. 36.

    Let's design the model in Reason! type ticTacToeState = {

    board: board progress: progress, }; type token = | X | O | Empty type player = | X | O type progress = 
 | Turn player
 | Win player
 | Draw type board = (row, row, row) type row = (token, token, token)
  34. 37.

    Model Types in Action (Reason) switch (progress) { | Turn

    p => "Player" ^ (p_to_str p) ^ "'s turn" | Win p => "Player" ^ (p_to_str p) ^ "won" | Draw => "It's a draw!" }; let progress = Turn X; switch (progress.type) { case "turn": const {player} = progress; return `Player ${player}'s turn`; case "win": const {player} = progress; return `Player ${player} won`; };
  35. 38.

    let parseGameJsonExn text => Js.Json.parseExn text |> (fun json =>

    Json.Decode.{ board: field "board" (array (array string)) json, progress: field "progress" (array string) json }) |> convertData;
  36. 39.

    let convertData {board, progress} :ticTacToeState => { board: parseBoard board,

    progress: parseProgress progress }; function convertData(obj: { board: Array<Array string>, progress: Array<string> }): TicTacToeState
  37. 40.

    let fetchData () => Js.Promise.( fetch "/load" |> then_ Response.text

    |> then_ (fun text => try (parseGameJsonExn text |> resolve) { | ex => reject ex }) ); function fetchData (): Promise<TicTacToeState>
  38. 41.

    // src/ticTacToe.re open Game; type action = | PlayTurn selection

    | Restart; type state = ticTacToeState; let component = ReasonReact.reducerComponent "TicTacToe"; let make ::board=? ::progress=? _children => { ...component, initialState: fun () => { ... }, reducer: fun action state => switch action { | PlayTurn s => ReasonReact.Update (playTurn state s) | Restart => ReasonReact.Update (initialState ()) }, render: fun {state, reduce} => { ... } };
  39. 42.

    ... render: fun {state, reduce} => <div className="game"> <div className="game-board">

    <Board rows=state.board handleSquareClick=( reduce (fun selection => PlayTurn selection) ) /> </div> ( switch state.progress { | Turn p => renderCurrentPlayer p | Win p => renderWin p (reduce (fun _evt => Restart)) | Draw => renderDraw (reduce (fun _evt => Restart)) } ) </div> ...
  40. 43.
  41. 44.

    type props = { value: token, handleClick: ReactEventRe.Mouse.t => unit

    }; let component = ReasonReact.statelessComponent "Square"; let squareStyle = ReactDOMRe.Style.make width::"25px" fontSize::"100pt" (); let make ::value ::handleClick _children => { ...component, render: fun _self => <span style=squareStyle onClick=handleClick> ( switch value { | Cross => <div> X </div> | Circle => <div> O </div> | Empty => <div/> } ) </span> };
  42. 46.
  43. 47.