以前の記事でFlowの導入が完了し、Reactを使って書いたコードに型を付けることが出来るようになった。
ここからは実際に、Reactのコンポーネントに型を付けていく。
Flow導入の記事はこちら。
なお、この記事を書いている環境のFlowのバージョンは、0.46.0
。
対象となるコンポーネントを用意
以下のようなコンポーネントを作成した。
このコンポーネントに、型を付けていく。
class App extends React.Component { render() { return ( <div>{this.props.name}</div> ); } }
<App name="Tom" />
のようにname
をpropsとして渡して使うだけの、シンプルなコンポーネント。
最初に、このファイルの一番上に// @flow
と記述する。
この記述があるファイルのみが、Flowによるチェックの対象となる。
propsに型を付ける
まずはprops.name
に、型をつけてみる。
下記のようにprops:
と記述すればいい。
class App extends React.Component { props: { name: string, }; render() { return ( <div>{this.props.name}</div> ); } }
プロパティの名前: 型
と定義するのが、基本。
型の種類は多数あり、公式ドキュメントで確認できる。
Type Annotations | Flow
上記ではname
の型として文字列を指定しており、<App name={true} />
のように文字列以外を渡してFlowを実行すると、エラーになる。
次に、App
を次のように書き換えてみる。
class App extends React.Component { props: { name: string, } render() { return ( <div> {this.props.name}<br /> {this.props.age} </div> ); } }
そして、<App name="Tom" age={30} />
としてname
とage
を渡す。
この状態でFlowを実行するとどうなるか。
age
の型が定義されていないとして、エラーになる。
これを解消するには、age
の型も設定すればいい。
props: { name: string, age: number, }
このように、型を指定すれば、エラーは消える。
ではこの状態で<App name="Tom" />
としてage
を渡さなかった場合、どうなるか。
やはりエラーになる。
上記の設定だと、「age
にはかならずnumber
が渡される」ことを意味しており、他のデータ型が渡されたときはもちろん、値が渡されなかったときにもエラーとなる。
このような、値が渡されたときの型は決まっているが、値が渡されないこともある、というケースでは、以下のように設定すればよい。
props: { name: string, age?: number, }
?
がついているが、これはOptional Propertyというもので、このプロパティは文字通りオプショナルになる。
つまり、値を渡しても渡さなくてもよい。
これで、先程のエラーは消える。
Optional object type properties
論理和
要するにOR
。
これは何も難しいことはなく、プロパティの名前: 型 | 型
とすればいい。
type Prop = { name: string, age: number | string, };
このように書くと、age
は数値か文字列のいずれか、という指定になる。
そのため<App name="Tom" age="30" />
でもエラーにならない。
独自の型定義
自分で型を定義することも出来る。
type
で宣言して、定義する。
下記では、Prop
という型を定義し、それをApp
のprops
に設定している。
type Prop = { name: string, age: number, }; class App extends React.Component { props: Prop; render() { return ( <div> {this.props.name}<br /> {this.props.age} </div> ); } }
型の定義が長くなるようであれば、このほうが可読性が増すかもしれない。
何より、型を再利用することが出来る。
同じデータ構造が何度も出てくるのなら当然、一箇所で定義して使いまわしたほうがいい。
export
することも出来るので、ファイルをまたがって使うことが出来る。
stateに型を付ける
App
を拡張してstate
やメソッドを追加し、state
に型を付けたのが、以下。
type Prop = { name: string, age: number, }; type State = { isYouth: boolean, }; class App extends React.Component { constructor(props) { super(props); this.state = { isYouth: this.isYouth() }; } state: State; props: Prop; isYouth() { return this.props.age < 30; } render() { return ( <div> {this.props.name}<br /> {this.props.age} </div> ); } }
state
の場合も、props
の場合と何も変わらない。
メソッドにも型をつける
先程の状態でFlowを実行してもエラーにはならないが、メソッドにも型をつけたほうが望ましい。
まず、constructor
。
引数として渡されるprops
の型を、Prop
として指定している。
constructor(props: Prop) { super(props); this.state = { isYouth: this.isYouth() }; }
次にisYouth
。返り値は真偽値であると指定している。
isYouth(): boolean { return this.props.age < 30; }
Stateless Functinal Componenens
Reactのコンポーネントは関数で定義することも出来るが、それに対しては、次のように型を付ける。
const App = (props: { name: string, age: number }) => ( <div> {props.name}<br /> {props.age} </div> );