Upgrade to Pro — share decks privately, control downloads, hide ads and more …

flowで始める型のあるJavaScript

 flowで始める型のあるJavaScript

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

Sota Sugiura

June 10, 2017
Tweet

More Decks by Sota Sugiura

Other Decks in Technology

Transcript

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

    Single Page Application • WebRTC (P2P) • PWA • and more, more, more…
  2. 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
  3. 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. Precision / 精度 • JavaScriptのバグは⾒見見逃してはいけない • でもエラーを報告しすぎるツールは • 必要最低限のクリティカルなバグを報告する •

    精度をあげることで開発ツールの構築も可能 • 例例. IDEによる型補完 https://flow.org/en/docs/lang/#toc-precision
  5. function add(a, b) { return a + b; } add("1",

    2); Normal JavaScript ⾔言語仕様的には問題ないが…
  6. with flow annotation • サンプルコード // @flow function add(a: number,

    b: number): number { return a + b; } add("1", 2); flowの解析対象の⽬目印
  7. with flow annotation • サンプルコード // @flow function add(a: number,

    b: number): number { return a + b; } add("1", 2); 引数の 明示的型宣⾔言
  8. with flow annotation • サンプルコード // @flow function add(a: number,

    b: number): number { return a + b; } add("1", 2); 関数の返り値の 明示的型宣⾔言
  9. with flow annotation • サンプルコード // @flow function add(a: number,

    b: number): number { return a + b; } add("1", 2); Is it OK…?
  10. Work for not only annotation console.log("1" * 1); function printResponse(data)

    { console.log(data.res); } printResponse('string response');
  11. Work for not only annotation console.log("1" * 1); // Error!

    function printResponse(data) { console.log(data.res); } printResponse('string response');
  12. Work for not only annotation console.log("1" * 1); // Error!

    function printResponse(data) { console.log(data.res); } printResponse('string response'); // Error!
  13. Sample code function makeMemo(title, text) { const memo = {

    title: title, text: text, }; return memo; }
  14. Start flow // @flow function makeMemo(title, text) { const memo

    = { title: title, text: text, }; return memo; }
  15. Start flow // @flow function makeMemo(title, text) { const memo

    = { title: title, text: text, }; return memo; } flow導⼊入の 第⼀一歩
  16. Declare type // @flow function makeMemo(title: string, text: ?string): Object

    { const memo = { title: title, text: text, }; return memo; }
  17. Declare type // @flow function makeMemo(title: string, text: ?string): Object

    { const memo = { title: title, text: text, }; return memo; } 引数の型宣⾔言
  18. Declare type // @flow function makeMemo(title: string, text: ?string): Object

    { const memo = { title: title, text: text, }; return memo; } 引数の型宣⾔言 返り値の型宣⾔言
  19. Fix // @flow function makeMemo(title: string, text: ?string): Object {

    const memo = { title: title, text: `${title}\n${text}`, }; return memo; } textの⼿手前に改⾏行行を挟んで タイトルを挿⼊入
  20. 修正ポイントを探す // @flow function makeMemo(title: string, text: ?string): Object {

    const memo = { title: title, text: `${title}\n${text}`, }; return memo; } Oh, text is nullable!
  21. 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で ないことをチェックする
  22. 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; } 引数追加 オブジェクトに反映
  23. 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; } しかしここに意図しない⽂文字 列列が⼊入っても気がつけない…
  24. Original type // @flow type Fukuoka = 'mentaiko'; const omiyage:

    Fukuoka = 'mentaiko'; const nisemono: Fukuoka = 'takoyaki';
  25. Original type // @flow type Fukuoka = 'mentaiko'; const omiyage:

    Fukuoka = 'mentaiko'; // Works! const nisemono: Fukuoka = 'takoyaki'; // Error
  26. ENUM // @flow // Use or statement for type type

    MemoStatus = 'public' | 'private' | 'draft';
  27. ENUM // @flow // Use or statement for type type

    MemoStatus = 'public' | 'private' | 'draft'; どれか1つに当てはまれば MemoStatusとして識別される
  28. 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; }
  29. 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; } 期待する値の型を宣⾔言
  30. 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; } 期待する値の型を宣⾔言 引数に型宣⾔言
  31. コードとにらめっこ // @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; }
  32. コードとにらめっこ // @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. Object type // @flow type Item = { id: number,

    name: string, description?: string, }
  34. Object type // @flow type Item = { id: number,

    name: string, description?: string, } const validItem: Item = { id: 32, name: 'item_name', }; // Works validItem.description = 'description'; // Works
  35. 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
  36. 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 型違反
  37. Don’t allow extra key // @flow type Item = {|

    id: number, name: string, |} 定義していないキーを 許容しない
  38. Don’t allow extra key // @flow type Item = {|

    id: number, name: string, |} const validItem: Item = { id: 32, name: 'item_name', extraProp: 'extra!', };
  39. Don’t allow extra key // @flow type Item = {|

    id: number, name: string, |} const validItem: Item = { id: 32, name: 'item_name', extraProp: 'extra!', // Error }; 型宣⾔言していないキーが エラーになる
  40. 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, 型として宣⾔言できそう
  41. 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, |}
  42. 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; }
  43. 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; }
  44. 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; } ҙਤ͠ͳ͍ܕɺ஋͕ ౉͞Εͯ΋ؾ͚ͮͳ͍
  45. 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ͷߏ଄͕ มΘͬͯ΋࡯஌Ͱ͖ͳ͍
  46. 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; }
  47. 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͕ڭ͑ͯ͘ΕΔ
  48. 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ͷߏ଄͕໌֬ʹ
  49. 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ͷߏ଄͕໌֬ʹ ಠࣗͷܕఆٛͰ ΑΓݎ࿚ͳίʔσΟϯάΛ