Slide 1

Slide 1 text

Type-Safe Flux
 Using Flowtype @joe_re

Slide 2

Slide 2 text

Who am I? • twitter: @joe_re • github: @joe-re • working in freee K.K

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Static type checking in JavaScript

Slide 5

Slide 5 text

• TypeScript or • Flowtype or • …Dart?

Slide 6

Slide 6 text

Why do we need a static type checking?

Slide 7

Slide 7 text

Benefits of static type checking • Improve code readability • Explicitly declaring interface makes it easier to understand the intentions behind code • By realtime code check, get fast feedback cycle of developing JavaScript

Slide 8

Slide 8 text

Very effective in team development

Slide 9

Slide 9 text

What is Flowtype? • Type checking tool created by Facebook • Can introduce static type checking in JavaScript • Made with OCaml

Slide 10

Slide 10 text

Features • Strong type inference • Fast check in development cycle
 (monitor changes, and since only the ones that have changed are checked for the second time and thereafter) • Not AltJS
 (It provides only type annotation)

Slide 11

Slide 11 text

Strong type inference // @flow function foo(x) { return x * 10; } foo('Hello, world!'); qPX qPXKT GPP )FMMP XPSME ????????????????????GVODUJPODBMM SFUVSOY ?TUSJOH5IJTUZQFJTJODPNQBUJCMFXJUI SFUVSOY ??????OVNCFS

Slide 12

Slide 12 text

feel like OCaml let foo x = x * 10;; # val foo : int -> int =

Slide 13

Slide 13 text

With Flux

Slide 14

Slide 14 text

Interface in Flux ActionCreators notify Action to Store(via Diapatcher) ReactViews listen change events of Store ReactViews call ActionCreator

Slide 15

Slide 15 text

Demo Application • https://github.com/joe-re/typesafe-flux- sample

Slide 16

Slide 16 text

Demo app use flux-utils (but I guess that it can be applied in other frameworks with the same way of thinking.)

Slide 17

Slide 17 text

Definition of Action Creator // @flow import Dispatcher from './Dispatcher'; type CREATE = { type: 'create', comment: string }; type DELETE = { type: 'delete', id: number }; export type ActionTypes = CREATE | DELETE; function dispatch(params: ActionTypes) { Dispatcher.dispatch(params); } export default { create(comment: string) { dispatch({ type: 'create', comment }); }, delete(id: number) { dispatch({ type: 'delete', id }); } }; define actions and UnionTypes dispatch function receives UnionTypes dispatch’s args is always fulfill either definition of UnionTypes

Slide 18

Slide 18 text

ReactViews -> ActionCreator route is Type-Safe class CommentContainer extends React.Component { … handleCreateComment(comment: string) { CommentActions.create(1); } } $ flow scripts/CommentContainer.jsx:27 27: CommentActions.create(1); ^ number. This type is incompatible with the expected param type of 14: create(comment: string) { ^^^^^^ string. See: scripts/CommentActions.js:14 Found 1 error create method is expected to receive string

Slide 19

Slide 19 text

Definition of Store(1) // @flow import { ReduceStore } from 'flux/utils'; import type { ActionTypes } from './CommentActions'; import Dispatcher from './Dispatcher'; export type Comment = {id: number, comment: string }; export type State = Comment[]; let count = 0; class CommentStore extends ReduceStore { getInitialState(): State { return []; } reduce(state: State, action: ActionTypes): State { switch (action.type) { case 'create': return state.concat({ id: ++count, comment: action.comment }); case 'delete': const deleteId = action.id; return state.filter((v) => v.id !== deleteId); default: return state; } } } const instance = new CommentStore(Dispatcher); export default instance; import UnionTypes of Actions set action’s type as UnionTypes enable to narrow down UnionTypes can access only narrowed type’s property

Slide 20

Slide 20 text

ActionCreators -> Store route is Type-Safe // @flow class CommentStore extends ReduceStore { //… reduce(state: State, action: ActionTypes): State { switch (action.type) { case 'create': return state.concat({ id: ++count, comment: action.id }); case 'delete': const deleteId = action.id; return state.filter((v) => v.id !== deleteId); default: return state; } } $ flow scripts/CommentStore.js:20 20: return state.concat({ id: ++count, comment: action.id }); ^^ property `id`. Property not found in 20: return state.concat({ id: ++count, comment: action.id }); ^^^^^^ object type create action doesn’t have id property

Slide 21

Slide 21 text

Definition of Store(2) // @flow //… export type Comment = {id: number, comment: string }; export type State = Comment[]; //… class CommentStore extends ReduceStore { //… } define state of Store give State as generics declare module 'flux/utils' { declare class ReduceStore { getState(): T; getDispatchToken(): string; } declare class Container { static create(): any; } //… } use generics for public API

Slide 22

Slide 22 text

Store -> ReactViews route is Type-Safe // @flow import type { Comment } from './CommentStore'; type State = { comments: Comment[] }; class CommentContainer extends React.Component { state: State; //… static calculateState(_prevState: State): State { const comments = CommentStore.getState(); return { comments }; } //… } can get typed state of Store

Slide 23

Slide 23 text

The following routes got type-safe • ReactViews call ActionCreator
 (ReactViews -> ActionCreator) • ActionCreators notify Action to Store
 (ActionCreators -> Store) • ReactViews listen change events of Store
 (Store -> ReactViews)

Slide 24

Slide 24 text

One more thing.. ActionCreators call WebAPI

Slide 25

Slide 25 text

We need WebAPI Utils layer • Action Creator === User Interaction • Some WebAPI may be called from multiple ActionCreators • Sometimes, WebAPI are called by Store • DRY…

Slide 26

Slide 26 text

Definition of WebAPI Util // @flow import request from './request'; import type { Foo } from 'types/foo'; function toCreateParans(foo: Foo) { /* some logic */ } type GetFoos = { foos: Array }; const getFoos = (): Promise => { return request.get({ url: '/api/v1/foo' }); }; type CreateFooParams = { foo: Foo };
 type CreateFooResponse = { foo: Foo, bar: Bar }; const createFoo = (params: CreateFooParams): Promise< CreateFooResponse> => { const { foo } = params; return request.post({ url: '/api/p/v2/expense_applications', parameters: toRequestObject(foo) }); }; //... export default { getFoos, createFoo, /* ... */ }; import ajax lib
 (fetch or bluebird or super agent or…) set return value's type as Promise
 (wrap APIResponse type) define ApiUtil’s interface

Slide 27

Slide 27 text

ActionCreators <-> WebAPI is Type-Safe // @flow //.. create(params: { foo: Foo }) { const { Foo } = params; FooAPI.createFoo({ foo }) .then((res) => { dispatch({ type: 'foo/success_create', foo: res.foo, bar: res.bar }); }).catch((data) => { //... });; }, became type-safe interface (Request and Response)

Slide 28

Slide 28 text

All Clear!

Slide 29

Slide 29 text

We got Type-Safe Flux


Slide 30

Slide 30 text

΢ΟʔΞʔϋΠΞϦϯά http://jobs.jobvite.com/freee/jobs

Slide 31

Slide 31 text

Thanks for your attention!