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

flowで始める型のあるJavaScript

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

 flowで始める型のあるJavaScript

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

Avatar for Sota Sugiura

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ͷߏ଄͕໌֬ʹ ಠࣗͷܕఆٛͰ ΑΓݎ࿚ͳίʔσΟϯάΛ