flowで始める型のあるJavaScript

 flowで始める型のあるJavaScript

PHPカンファレンス福岡2017で発表しました。

046baac588d91fd78a85b189847a151d?s=128

Sota Sugiura

June 10, 2017
Tweet

Transcript

  1. 14.

    最新のWebサイト • WebVR • WebGL • Web Audio API •

    Single Page Application • WebRTC (P2P) • PWA • and more, more, more…
  2. 18.
  3. 28.

    Goal of flow Flow is a static type checker for

    JavaScript that we built at Facebook. The overall mission of Flow is to deliver an immersive coding experience for JavaScript developers—a fast edit-refresh cycle—even as the codebase evolves and grows. In engineering terms, we identify two concrete goals that are important to this mission: precision and speed. These goals pervasively drive the design and implementation. https://flow.org/en/docs/lang/#toc-flow-goals
  4. 29.

    Goal of flow Flow is a static type checker for

    JavaScript that we built at Facebook. The overall mission of Flow is to deliver an immersive coding experience for JavaScript developers—a fast edit-refresh cycle—even as the codebase evolves and grows. In engineering terms, we identify two concrete goals that are important to this mission: precision and speed. These goals pervasively drive the design and implementation. https://flow.org/en/docs/lang/#toc-flow-goals
  5. 31.

    Precision / 精度 • JavaScriptのバグは⾒見見逃してはいけない • でもエラーを報告しすぎるツールは • 必要最低限のクリティカルなバグを報告する •

    精度をあげることで開発ツールの構築も可能 • 例例. IDEによる型補完 https://flow.org/en/docs/lang/#toc-precision
  6. 34.

    function add(a, b) { return a + b; } add("1",

    2); Normal JavaScript ⾔言語仕様的には問題ないが…
  7. 37.
  8. 38.

    with flow annotation • サンプルコード // @flow function add(a: number,

    b: number): number { return a + b; } add("1", 2); flowの解析対象の⽬目印
  9. 39.

    with flow annotation • サンプルコード // @flow function add(a: number,

    b: number): number { return a + b; } add("1", 2); 引数の 明示的型宣⾔言
  10. 40.

    with flow annotation • サンプルコード // @flow function add(a: number,

    b: number): number { return a + b; } add("1", 2); 関数の返り値の 明示的型宣⾔言
  11. 41.

    with flow annotation • サンプルコード // @flow function add(a: number,

    b: number): number { return a + b; } add("1", 2); Is it OK…?
  12. 44.

    Work for not only annotation console.log("1" * 1); function printResponse(data)

    { console.log(data.res); } printResponse('string response');
  13. 45.

    Work for not only annotation console.log("1" * 1); // Error!

    function printResponse(data) { console.log(data.res); } printResponse('string response');
  14. 46.

    Work for not only annotation console.log("1" * 1); // Error!

    function printResponse(data) { console.log(data.res); } printResponse('string response'); // Error!
  15. 58.

    Sample code function makeMemo(title, text) { const memo = {

    title: title, text: text, }; return memo; }
  16. 60.

    Start flow // @flow function makeMemo(title, text) { const memo

    = { title: title, text: text, }; return memo; }
  17. 61.

    Start flow // @flow function makeMemo(title, text) { const memo

    = { title: title, text: text, }; return memo; } flow導⼊入の 第⼀一歩
  18. 63.

    Declare type // @flow function makeMemo(title: string, text: ?string): Object

    { const memo = { title: title, text: text, }; return memo; }
  19. 64.

    Declare type // @flow function makeMemo(title: string, text: ?string): Object

    { const memo = { title: title, text: text, }; return memo; } 引数の型宣⾔言
  20. 65.

    Declare type // @flow function makeMemo(title: string, text: ?string): Object

    { const memo = { title: title, text: text, }; return memo; } 引数の型宣⾔言 返り値の型宣⾔言
  21. 67.

    Fix // @flow function makeMemo(title: string, text: ?string): Object {

    const memo = { title: title, text: `${title}\n${text}`, }; return memo; } textの⼿手前に改⾏行行を挟んで タイトルを挿⼊入
  22. 70.

    修正ポイントを探す // @flow function makeMemo(title: string, text: ?string): Object {

    const memo = { title: title, text: `${title}\n${text}`, }; return memo; } Oh, text is nullable!
  23. 71.

    Check argument // @flow function makeMemo(title: string, text: ?string): Object

    { let content = text; if (text) { content = `${title}\n${text}`; } const memo = { title: title, text: content, }; return memo; } textがnull, undefinedで ないことをチェックする
  24. 72.
  25. 75.

    Add argument // @flow function makeMemo(title: string, status: string, text:

    ?string): Object { let content = text; if (text) { content = `${title}\n${text}`; } const memo = { title: title, text: content, status: status, }; return memo; } 引数追加 オブジェクトに反映
  26. 76.

    Add argument // @flow function makeMemo(title: string, status: string, text:

    ?string): Object { let content = text; if (text) { content = `${title}\n${text}`; } const memo = { title: title, text: content, status: status, }; return memo; } しかしここに意図しない⽂文字 列列が⼊入っても気がつけない…
  27. 78.
  28. 82.

    Original type // @flow type Fukuoka = 'mentaiko'; const omiyage:

    Fukuoka = 'mentaiko'; const nisemono: Fukuoka = 'takoyaki';
  29. 83.

    Original type // @flow type Fukuoka = 'mentaiko'; const omiyage:

    Fukuoka = 'mentaiko'; // Works! const nisemono: Fukuoka = 'takoyaki'; // Error
  30. 84.

    ENUM // @flow // Use or statement for type type

    MemoStatus = 'public' | 'private' | 'draft';
  31. 85.

    ENUM // @flow // Use or statement for type type

    MemoStatus = 'public' | 'private' | 'draft'; どれか1つに当てはまれば MemoStatusとして識別される
  32. 86.

    Use ENUM // @flow type MemoStatus = 'public'|'private'|'draft'; function makeMemo(title:

    string, status: MemoStatus, text: ?string): Object { let content = text; if (text) { content = `${title}\n${text}`; } const memo = { title: title, text: content, status: status, }; return memo; }
  33. 87.

    Use ENUM // @flow type MemoStatus = 'public'|'private'|'draft'; function makeMemo(title:

    string, status: MemoStatus, text: ?string): Object { let content = text; if (text) { content = `${title}\n${text}`; } const memo = { title: title, text: content, status: status, }; return memo; } 期待する値の型を宣⾔言
  34. 88.

    Use ENUM // @flow type MemoStatus = 'public'|'private'|'draft'; function makeMemo(title:

    string, status: MemoStatus, text: ?string): Object { let content = text; if (text) { content = `${title}\n${text}`; } const memo = { title: title, text: content, status: status, }; return memo; } 期待する値の型を宣⾔言 引数に型宣⾔言
  35. 90.

    コードとにらめっこ // @flow type MemoStatus = 'public'|'private'|'draft'; function makeMemo(title: string,

    status: MemoStatus, text: ?string): Object { let content = text; if (text) { content = `${title}\n${text}`; } const memo = { title: title, text: content, status: status, }; return memo; }
  36. 91.

    コードとにらめっこ // @flow type MemoStatus = 'public'|'private'|'draft'; function makeMemo(title: string,

    status: MemoStatus, text: ?string): Object { let content = text; if (text) { content = `${title}\n${text}`; } const memo = { title: title, text: content, status: status, }; return memo; } ここの型が曖昧すぎる
  37. 93.

    Object type // @flow type Item = { id: number,

    name: string, description?: string, }
  38. 94.

    Object type // @flow type Item = { id: number,

    name: string, description?: string, } const validItem: Item = { id: 32, name: 'item_name', }; // Works validItem.description = 'description'; // Works
  39. 95.

    Object type // @flow type Item = { id: number,

    name: string, description?: string, } const validItem: Item = { id: 32, name: 'item_name', }; // Works validItem.description = 'description'; // Works validItem.id = '32'; // Error
  40. 96.

    Object type // @flow type Item = { id: number,

    name: string, description?: string, } const validItem: Item = { id: 32, name: 'item_name', }; // Works validItem.description = 'description'; // Works validItem.id = '32'; // Error 型違反
  41. 97.
  42. 98.

    Don’t allow extra key // @flow type Item = {|

    id: number, name: string, |} 定義していないキーを 許容しない
  43. 99.

    Don’t allow extra key // @flow type Item = {|

    id: number, name: string, |} const validItem: Item = { id: 32, name: 'item_name', extraProp: 'extra!', };
  44. 100.

    Don’t allow extra key // @flow type Item = {|

    id: number, name: string, |} const validItem: Item = { id: 32, name: 'item_name', extraProp: 'extra!', // Error }; 型宣⾔言していないキーが エラーになる
  45. 101.

    It’s time to fix! // @flow type MemoStatus = 'public'|'private'|'draft';

    function makeMemo(title: string, status: MemoStatus, text: ?string): Object { let content = text; if (text) { content = `${title}\n${text}`; } const memo = { title: title, text: content, status: status, }; return memo; } このObject, 型として宣⾔言できそう
  46. 102.

    It’s time to fix! // @flow type MemoStatus = 'public'|'private'|'draft';

    function makeMemo(title: string, status: MemoStatus, text: ?string): Object { let content = text; if (text) { content = `${title}\n${text}`; } const memo = { title: title, text: content, status: status, }; return memo; } // @flow type Memo = {| title: string, text: ?string, status: MemoStatus, |}
  47. 103.

    It’s time to fix! // @flow type MemoStatus = 'public'|'private'|'draft';

    type Memo = {| title: string, text: ?string, status: MemoStatus, |} function makeMemo(title: string, status: MemoStatus, text: ?string): Memo { let content = text; if (text) { content = `${title}\n${text}`; } const memo: Memo = { title: title, text: content, status: status, }; return memo; }
  48. 105.

    Without flow function makeMemo(title, status, text) { let content =

    text; if (text) { content = `${title}\n${text}`; } const memo = { title: title, text: content, status: status, }; return memo; }
  49. 106.

    Without flow function makeMemo(title, status, text) { let content =

    text; if (text) { content = `${title}\n${text}`; } const memo = { title: title, text: content, status: status, }; return memo; } ҙਤ͠ͳ͍ܕɺ஋͕ ౉͞Εͯ΋ؾ͚ͮͳ͍
  50. 107.

    Without flow function makeMemo(title, status, text) { let content =

    text; if (text) { content = `${title}\n${text}`; } const memo = { title: title, text: content, status: status, }; return memo; } ҙਤ͠ͳ͍ܕɺ஋͕ ౉͞Εͯ΋ؾ͚ͮͳ͍ 0CKFDUͷߏ଄͕ มΘͬͯ΋࡯஌Ͱ͖ͳ͍
  51. 108.

    With flow // @flow type MemoStatus = 'public'|'private'|'draft'; type Memo

    = {| title: string, text: ?string, status: MemoStatus, |} function makeMemo(title: string, status: MemoStatus, text: ?string): Memo { let content = text; if (text) { content = `${title}\n${text}`; } const memo: Memo = { title: title, text: content, status: status, }; return memo; }
  52. 109.

    With flow // @flow type MemoStatus = 'public'|'private'|'draft'; type Memo

    = {| title: string, text: ?string, status: MemoStatus, |} function makeMemo(title: string, status: MemoStatus, text: ?string): Memo { let content = text; if (text) { content = `${title}\n${text}`; } const memo: Memo = { title: title, text: content, status: status, }; return memo; } ҙਤ͠ͳ͍ܕɺ஋͕ ౉͞ΕͨΒqPX͕ڭ͑ͯ͘ΕΔ
  53. 110.

    With flow // @flow type MemoStatus = 'public'|'private'|'draft'; type Memo

    = {| title: string, text: ?string, status: MemoStatus, |} function makeMemo(title: string, status: MemoStatus, text: ?string): Memo { let content = text; if (text) { content = `${title}\n${text}`; } const memo: Memo = { title: title, text: content, status: status, }; return memo; } ҙਤ͠ͳ͍ܕɺ஋͕ ౉͞ΕͨΒqPX͕ڭ͑ͯ͘ΕΔ ಠࣗఆٛͷܕએݴʹΑΓ 0CKFDUͷߏ଄͕໌֬ʹ
  54. 111.

    With flow // @flow type MemoStatus = 'public'|'private'|'draft'; type Memo

    = {| title: string, text: ?string, status: MemoStatus, |} function makeMemo(title: string, status: MemoStatus, text: ?string): Memo { let content = text; if (text) { content = `${title}\n${text}`; } const memo: Memo = { title: title, text: content, status: status, }; return memo; } ҙਤ͠ͳ͍ܕɺ஋͕ ౉͞ΕͨΒqPX͕ڭ͑ͯ͘ΕΔ ಠࣗఆٛͷܕએݴʹΑΓ 0CKFDUͷߏ଄͕໌֬ʹ ಠࣗͷܕఆٛͰ ΑΓݎ࿚ͳίʔσΟϯάΛ
  55. 115.