JSX がサポートされた TypeScript 1.6 がいよいよ beta になったので,いくつかの趣味プロジェクトに投入してみた感想です.
バグを踏むことも無く,基本的には JSX のときと同じように書けました.付けなければいけない props の指定し忘れや state の型の間違いが無いかをコンパイラがチェックしてくれます.React にも props をバリデーションするなどの機能はありますが,コンパイル時にそのあたり見てくれると便利.
Real world example のうちの1つとして,Electron 向けの埋め込みブラウザのコンポーネントを TypeScript + React で書きました.commonjs モジュールで ES5 向けにコンパイルした後,browserify で結合する感じです.
基本的な使い方はもっと以前に試しておられる方がいらっしゃるのでそちらを見てもらえれば使い方は変わっていないので大丈夫です.
React JSX with TypeScript(1.6)
ここでは微妙にハマったポイントとか今後の改善ポイントとか,実はこれもっとうまく書けるんじゃないのかなとか思った箇所を5点書きます.
インポート文
React に限りませんが,TypeScript のインポート文は ES6 のそれとデフォルトインポートの挙動が少し異なっています.
import React from 'react'
import * as React from 'react'
特別な意味のある props の定義
React コンポーネントは型定義が React.Component<Props, State> となっているため,ノードを一意に特定するための this.props.key という property や,子ノードを表す this.props.children といった property も現状手で型定義する必要があります.
interface Props {
key?: string;
children?: React.ReactElement<any>[];
}
これはもっと react.d.ts が良くなって,interface Props extends React.PropsBase みたいに書けば自動で定義してくれるようになるとうれしいなと思います.
ref の定義
react.d.ts における this.refs の型定義は現状 refs: { [key: string]: React.Component<any, any> } となっているため,this.refs.foo のような書き方ができず,this.refs['foo'] と書く必要があるようです.これは良くない…
個人的にはコンポーネントの型が React.Component<Props, State, Ref> になって,interface MyRefs {foo: some_type} みたいに書けるのが良さそうに思います.
また,上記の props の問題と同様に,自前で定義したコンポーネントの Props の定義の中に ref メンバを含めておかないと,そのコンポーネントを使う側では ref="..." のように指定できません.なお,デフォルトで使える <div> などの要素は ref が定義されているため気にする必要はありません.
interface Props {
ref: string;
}
class MyComponent extends React.Component<Props, {}> {
// ...
}
// ここで ref を使うには上記の Props 内の定義が必要
class SomeComponent extends React.Component<{}, {}> {
// ...
render() {
return <MyComponent ref="foo"/>;
}
}
onClick などのイベントの型
onClick などのイベントは React の event wrapper である React.SyntheticEvent になります.これは元々 React がそういう wrapper を用意しているので当たり前といえばそうですが,今までと同じ調子で MouseEvent を指定してしまいコンパイラに怒られました.
デフォルトプロパティ
React + ES6 では,defaultProps というプロパティをコンポーネントのコンストラクタに追加することで props の指定が無かった場合に React がデフォルトの値を props に書き込んでくれます.React のドキュメント に載っています.
TypeScript でもこれは static メンバ変数として書けます.
interface CounterProps {
count?: number;
}
class Counter extends React.Component<CounterProps, {}> {
public static defaultProps = {count: 0};
// ...
}
ただ,defaultProps の型と CounterProps の間の整合性はコンパイラはチェックしてくれません.(例えば count を cont にタイポしていてもコンパイラは気づきませんし,count: "42" のように型を間違えていても気づきません)
このあたり react.d.ts をもっとうまく書けば良い感じにチェックしてくれるようになるのかな…
まとめ
TypeScript 1.6 が beta になったので試しに使ってみました.react.d.ts の定義がまだ改善の余地があるなという感じですが,基本的にはかなり快適に使えていて babel で JSX 使うよりも書きやすいなぁという印象です.