Slide 1

Slide 1 text

flowで始める 型のあるJavaScript @sota1235 2017/6/10 PHPカンファレンス福岡2017

Slide 2

Slide 2 text

console.log(me); • Sota Sugiura • @sota1235 • JavaScript PHP • Mercari, Inc.

Slide 3

Slide 3 text

Why using JavaScript?

Slide 4

Slide 4 text

Why? • 私たちはPHPエンジニア • Webと関わる⼈人も多い • Webは⽇日々進化を続けている

Slide 5

Slide 5 text

最古のWebサイト • 1990年年11⽉月 • ペライチのHTML • ⽂文章配布のためのWeb

Slide 6

Slide 6 text

最新のWebサイト • インタラクティブなUI • ⾼高頻度のAjax通信 • リッチなブラウザアプリケーション

Slide 7

Slide 7 text

最新のWebサイト

Slide 8

Slide 8 text

最新のWebサイト Web Audio API

Slide 9

Slide 9 text

最新のWebサイト Web Audio API

Slide 10

Slide 10 text

最新のWebサイト Web Audio API

Slide 11

Slide 11 text

最新のWebサイト Web Audio API .PSF .PSF

Slide 12

Slide 12 text

最新のWebサイト Web Audio API .PSF .PSF .PSF .PSF .PSF .PSF

Slide 13

Slide 13 text

最新のWebサイト Web Audio API .PSF .PSF .PSF .PSF .PSF .PSF .PSF .PSF .PSF .PSF .PSF .PSF .PSF

Slide 14

Slide 14 text

最新のWebサイト • WebVR • WebGL • Web Audio API • Single Page Application • WebRTC (P2P) • PWA • and more, more, more…

Slide 15

Slide 15 text

どう実現するのか? • HTML, CSSでは役不不⾜足 • そもそも作られた⽬目的が違う

Slide 16

Slide 16 text

どう実現するのか? • HTML, CSSでは役不不⾜足 • そもそも作られた⽬目的が違う • JavaScriptこそが鍵

Slide 17

Slide 17 text

With JavaScript • 数々のブラウザのAPI • インタラクティブなUI • ⾮非同期なHTTP通信 • デバイス(カメラ、マイク、etc)へのアクセス

Slide 18

Slide 18 text

Not only on Website • Smartphone App (React Native) • Server Side JavaScript (Node.js)

Slide 19

Slide 19 text

Why using JavaScript? • ⽂文書配布のためのWebはとっくのとうに終 わってる • ちょっとリッチなWebサイトって時代ももう 終わってる • JavaScriptは多様な夢を実現する⼿手段として地 位を確⽴立しつつある

Slide 20

Slide 20 text

JavaScript無くして 次世代のWebは来ない

Slide 21

Slide 21 text

But… • 歴史的経緯に基づくツライ⾔言語仕様 • あまりに多い実⾏行行環境の多さ • 終わらないベンダー戦争 • 速すぎるとも⾔言える進化と成⻑⾧長

Slide 22

Slide 22 text

And more… ツラミと戦うために…

Slide 23

Slide 23 text

And more… ツラミと戦うために…

Slide 24

Slide 24 text

Today’s theme 今⽇日の話はそんな数ある解決策のうちの1つ、flowの話

Slide 25

Slide 25 text

About flow

Slide 26

Slide 26 text

flow • JavaScriptのための静的 型解析ツール • Facebook製 • OCamlで書かれている

Slide 27

Slide 27 text

flow is not AltJS • あくまで静的型解析ツール https://flow.org/en/

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Goal of flow • コードの成⻑⾧長に負けない開発スピードを保ちたい • flowはその⼿手助けをする • そのためにflowは精度と速度を重視する

Slide 31

Slide 31 text

Precision / 精度 • JavaScriptのバグは⾒見見逃してはいけない • でもエラーを報告しすぎるツールは • 必要最低限のクリティカルなバグを報告する • 精度をあげることで開発ツールの構築も可能 • 例例. IDEによる型補完 https://flow.org/en/docs/lang/#toc-precision

Slide 32

Slide 32 text

Speed / 速度 • 速度は精度と相反する • 速度の低下で開発サイクルを遅らせることは JavaScriptの魅⼒力力を損なうことに他ならない • コードを実⾏行行することなく精度の⾼高くバグを を⾒見見つけることがベスト https://flow.org/en/docs/lang/#toc-speed

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

function add(a, b) { return a + b; } add("1", 2); Normal JavaScript ⾔言語仕様的には問題ないが…

Slide 35

Slide 35 text

Bug? Or not? QP%JTQOGEQPUQNG

Slide 36

Slide 36 text

Bug? Or not? ほんとは”3”が欲しかった… QP%JTQOGEQPUQNG

Slide 37

Slide 37 text

with flow annotation • サンプルコード // @flow function add(a: number, b: number): number { return a + b; } add("1", 2);

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

with flow annotation • サンプルコード // @flow function add(a: number, b: number): number { return a + b; } add("1", 2); Is it OK…?

Slide 42

Slide 42 text

Error report by flow

Slide 43

Slide 43 text

Error report by flow number型引数に stringを渡さないでね

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

サポートする機能 • 関数の引数、返り値、変数の明示的型宣⾔言 • Interface • Generic Type • Comment Type • and so on :)

Slide 48

Slide 48 text

How to use? • flow-binをインストール • flow initしてconfig⽣生成 • 解析対象ファイルに // @flow • flowコマンド実⾏行行

Slide 49

Slide 49 text

スモールスタートなら5分で使える

Slide 50

Slide 50 text

When should we use flow?

Slide 51

Slide 51 text

こんなことで困ってませんか? • コードベースが巨⼤大 • テスト不不能 • でも負債返済の時間は⼗十分にない

Slide 52

Slide 52 text

こんなことで困ってませんか? • コードベースが巨⼤大 • テスト不不能 • でも負債返済の時間は⼗十分にない flowはこういう場⾯面で 威⼒力力を発揮できる

Slide 53

Slide 53 text

flowのいいところ(1) 導⼊入が簡単 • 解析するだけなら何もいらない • 必要なのはflowコマンドとJSファイルだけ • babelが導⼊入済みであればannotationも瞬時に 導⼊入可能 • 1ファイルずつ導⼊入できる

Slide 54

Slide 54 text

flowのいいところ(2) 捨てやすさ • flowはランタイム、コンパイルに関与しない • 捨てるときはflowコマンドの実⾏行行をやめるだけ • アノテーションを消したければflow-remove- typesを使う • https://flow.org/en/docs/tools/flow-remove-types/

Slide 55

Slide 55 text

flowが有効でない場⾯面 • ⼩小規模プロジェクトの場合 • 本当に型必要? • 1ファイルが巨⼤大過ぎる場合 • 新規プロジェクトの場合 • 有効だがTypeScriptの使⽤用も視野に

Slide 56

Slide 56 text

Let’s use flow

Slide 57

Slide 57 text

実際にflowを使ってみよう • サンプルコードにflowを導⼊入していく • 今回はシンプルに値を渡すとObjectを返す関 数をいじっていく

Slide 58

Slide 58 text

Sample code function makeMemo(title, text) { const memo = { title: title, text: text, }; return memo; }

Slide 59

Slide 59 text

復復習(1) flowの始め⽅方 • flowは全てのファイルを解析しない • // @flowが書いてあるファイルのみ解析する

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

Start flow // @flow function makeMemo(title, text) { const memo = { title: title, text: text, }; return memo; } flow導⼊入の 第⼀一歩

Slide 62

Slide 62 text

復復習(2) 明示的型宣⾔言 • flowでは引数、返り値の明示的型宣⾔言ができる • nullable型もサポートしている

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

天の声 その1 • ここで仕様変更更 • 上司「memoの本⽂文の頭にタイトルをくっつけてくれ」

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

Error occurred

Slide 69

Slide 69 text

Error occurred nullを⽂文字列列連結するな undefinedを⽂文字列列連結するな

Slide 70

Slide 70 text

修正ポイントを探す // @flow function makeMemo(title: string, text: ?string): Object { const memo = { title: title, text: `${title}\n${text}`, }; return memo; } Oh, text is nullable!

Slide 71

Slide 71 text

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で ないことをチェックする

Slide 72

Slide 72 text

Works!

Slide 73

Slide 73 text

天の声 その2 • ここでまたまた仕様変更更 • 上司「memoの公開/⾮非公開/下書きステータスを持たせて くれ」

Slide 74

Slide 74 text

仕様検討 • オブジェクトにstatusキーを追加する • statusにはpublic, private, draftのいずれかが⼊入る

Slide 75

Slide 75 text

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; } 引数追加 オブジェクトに反映

Slide 76

Slide 76 text

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; } しかしここに意図しない⽂文字 列列が⼊入っても気がつけない…

Slide 77

Slide 77 text

あーーーー 特定の⽂文字列列しか受け取らなくて 意図しない値が来たらエラーでコケるような 素敵な仕組みないかなーーーーーーーー

Slide 78

Slide 78 text

ENUM

Slide 79

Slide 79 text

flowでENUM • flowでは固定値を型として宣⾔言できる • また、複数の型をOR形式で宣⾔言できる • これを組み合わせるとENUMができる

Slide 80

Slide 80 text

Original type // @flow type Fukuoka = 'mentaiko';

Slide 81

Slide 81 text

Original type // @flow type Fukuoka = 'mentaiko'; type syntaxで 新たな型を定義できる

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

Original type // @flow type Fukuoka = 'mentaiko'; const omiyage: Fukuoka = 'mentaiko'; // Works! const nisemono: Fukuoka = 'takoyaki'; // Error

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

ENUM // @flow // Use or statement for type type MemoStatus = 'public' | 'private' | 'draft'; どれか1つに当てはまれば MemoStatusとして識別される

Slide 86

Slide 86 text

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; }

Slide 87

Slide 87 text

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; } 期待する値の型を宣⾔言

Slide 88

Slide 88 text

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; } 期待する値の型を宣⾔言 引数に型宣⾔言

Slide 89

Slide 89 text

天の声 その3 • なんとユーザからのバグ報告 • 上司「別のメソッドが返すオブジェクトと構造がバラバ ラだからなんとかして」

Slide 90

Slide 90 text

コードとにらめっこ // @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; }

Slide 91

Slide 91 text

コードとにらめっこ // @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; } ここの型が曖昧すぎる

Slide 92

Slide 92 text

Object type • Objectの構造を型として定義できる • キーをnullableとして定義できる • 余計なキーが⼊入らないように定義することも 可能

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

Object type // @flow type Item = { id: number, name: string, description?: string, } const validItem: Item = { id: 32, name: 'item_name', }; // Works validItem.description = 'description'; // Works

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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 型違反

Slide 97

Slide 97 text

Don’t allow extra key // @flow type Item = {| id: number, name: string, |}

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

Don’t allow extra key // @flow type Item = {| id: number, name: string, |} const validItem: Item = { id: 32, name: 'item_name', extraProp: 'extra!', // Error }; 型宣⾔言していないキーが エラーになる

Slide 101

Slide 101 text

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, 型として宣⾔言できそう

Slide 102

Slide 102 text

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, |}

Slide 103

Slide 103 text

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; }

Slide 104

Slide 104 text

It’s flow! • 型を宣⾔言することでコードが⾃自然と型安全に なっていく • ⼯工夫次第で堅牢で保守性の⾼高いJavaScriptを書 くことが可能

Slide 105

Slide 105 text

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; }

Slide 106

Slide 106 text

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; } ҙਤ͠ͳ͍ܕɺ஋͕ ౉͞Εͯ΋ؾ͚ͮͳ͍

Slide 107

Slide 107 text

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ͷߏ଄͕ มΘͬͯ΋࡯஌Ͱ͖ͳ͍

Slide 108

Slide 108 text

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; }

Slide 109

Slide 109 text

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͕ڭ͑ͯ͘ΕΔ

Slide 110

Slide 110 text

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ͷߏ଄͕໌֬ʹ

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

more and more • 今回話したのはflowのほんのほんの⼀一部 • ⼯工夫と発想次第でより表現豊かなコーディング ができる • 特にObject型の定義はJavaScriptと相性が抜群

Slide 113

Slide 113 text

終わりに

Slide 114

Slide 114 text

終わりに • flowは静的型解析ツール • ゆえに既存コードへの反映が⾮非常に簡単 • 1ファイルずつ導⼊入ができる、かつ捨てるのも ⼀一瞬 • 型プログラミングで堅牢で型安全なJavaScript を実現できる

Slide 115

Slide 115 text

Thank you!