/ react

Javascript に Flow を導入してみた

Flow とは

動的型付けのJavaScriptでも静的型付けができるようになる静的チェッカーです。
つまりJavascript でもTypescriptのように型宣言することができるようになります。

Flow インストール

こちらを参照

Flow 有効

ファイルの一番上に

// @flow

を追加する。

Flowのプリミティブ型

意味
any 静的チェック無効。何も書かないとこれ
mixed 何の型でも受け取るが、利用時に型ごとに絞込が必要
* (実存型) 代入のたびに強制的な型推論を行う
number 数値
boolean ブール
string 文字列
'A' | 'B' | 'C' AかBかCのどれか
Object オブジェクト
{ id: number } id を持つオブジェクト
Array<string> 文字列の配列
Set<string> 文字列のSet
string[ ] 文字列の配列
Function 関数
Class<User> Userクラス
React.DOM React Element
SyntheticInputEvent<EventTarget> React.DOMのEvent

その他、膨大な数の型が載ってます。
https://www.saltycrane.com/flow-type-cheat-sheet/latest/

Flow の特殊な型

mixed

全ての型を代入できるが、利用するときはチェックが必要。

let val: mixed;
val = {};
val = 1;
val = 'test';
if (typeof val === 'function') {
  val();
}
val(); // error

* (実存型)

全ての型を代入できる。また、代入時に強制的に型推論される。

let val: *;
val = 1;          // val is number 
val = 'test';     // val is string 
val = {};         // val is Object
val();            // Error occurs because val is Object 
val = 1;          // val is number 
val.doSomething() // Error occurs because val is number

Maybe ( ? ) の使い方

null も undefined も許可しない

let val: number;
val = 0;
val = undefined; // error
val = null; // error

null と undefined を許可する

let val: ?number;
val = 0;
val = undefined; 
val = null; 

プロパティの必須有無

必須プロパティあり

let val: { id: number };
val = { id: 1 };
val = {  }; // error

必須プロパティなし

let val: { id?: number};
val = {id : 1};
val = { };

おまけ

必須プロパティなし。もしあった場合はnumberかnullかundefinedであること。

let val: { id?: ?number };
val = { id: 1 };
val = { };
val = { id: null };
val = { id: undefined };
val = { id: 'string' }; // error 

React + react-redux Component

e.g.

import React from 'react';
import { connect } from 'react-redux';
import type { MapStateToProps } from 'react-redux';
import { Map } from 'immutable';
import type { Map as MapType } from 'immutable';
import { fetchUsers } from 'actions/users';

type Props = {
  users: MapType,
  selected: string,
};

type State = {
  selected: string,
};

export default class UserSelector extends React.Component<Props, State> {
  static defaultProps = {
    users: new Map()
  };
  constructor(props: Props) {
    super(props);
    this.state = { selected: props.selected };
  }
  componentWillMount() {
    this.props.fetchUsers();
  }
  componentWillReceiveProps(nextProps: Props) {
    if (nextProps.selected) {
      this.setState({ selected: nextProps.selected });
    }
  }
  render() {
    this.props.users.valueSeq().map(user => (
      <div>
        {user.get('id') === this.state.selected && <p>{user.get('name')} selected</p>}
        {user.get('id') !== this.state.selected && <p>{user.get('name')}</p>}
      </div>
    ));
  }
};

const mapStateToProps: MapStateToProps<*, *, *> = (state: *) => {
  return {
    users: state.users,
    selected: state.selected,
  };
};

const mapDispatchToProps = (dispatch: Dispatch<*>) => ({
  fetchUsers : () => dispatch(fetchUsers())
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(UserSelector);

外部ライブラリの型

e.g. flow-typed でライブラリを検索してインストールする。

(´ω`) < npm run flow-typed search moment

> my-project@1.0.0 flow-typed /src
> flow-typed "search" "moment"

• rebasing flow-typed cache...done.

Found definitions:
╔════════╤═════════════════╤══════════════╗
║ Name   │ Package Version │ Flow Version ║
╟────────┼─────────────────┼──────────────╢
║ moment │ v2.3.x          │ >=v0.34.x    ║
╟────────┼─────────────────┼──────────────╢
║ moment │ v2.x.x          │ >=v0.28.x    ║
╚════════╧═════════════════╧══════════════╝

(´ω`) < npm run flow-typed install moment@v2.3.x  --skip

e.g. package.json から依存ライブラリの型情報をまとめてインストールする。

(´ω`) < npm run flow-typed install --skip

Learn more

Try flow playground
Flow cheatsheet
Flow Type Cheat Sheet