TypeScript の JSX/React サポートを試す


1.5.0-beta には入っていません (2015/07/21 追記: 1.5.3 にも入っていません。 1.6 で入る模様) が、 TypeScript の master ブランチでは、 JSX/React がサポートされています。

React/Flux の TodoMVC のサンプルコードを、これを使って TypeScript 化してみました。

コードはこちらから見られます。

こちらのサイトの説明を参考にさせていただきました。

ポイント

  • JSX を書くときは拡張子を .tsx にする
  • component は React.Component<P, S> を extends したクラスを作成する
    • P は props の型、 S は state の型
    • React.createClass で作ると、クラス内での this の参照がうまく解決できないので避けたほうがよいと思われる
  • getInitialState() は、 React.createClass で作ったクラスでしか機能せず、上記の方法でクラスを作った場合は使えない
    • メソッドを定義するとコンソールに warning が出力され、実際 state が undefined になってしまう
    • constructor で初期化すれば OK (constructor の引数が props なので、 state の初期値が props に依存する場合でも対応可)
  • tsc のオプションの --jsx
    • preserve: jsx の記法のまま (<div/> -> <div/>)
    • react : react のメソッドを使った記法に変換される (<div/> -> React.createElement("div"))
    • サンプルコードでは、 JS 変換後に reactify とかするのが面倒だったので、 --jsx react しました

サンプル

一例として、 TodoTextInput.tsx のコードを貼っておきます。

/// <reference path="../../typings/tsd.d.ts" />
import * as React from 'react';

let ENTER_KEY_CODE = 13;

export interface Props {
    className?: string;
    id?: string;
    placeholder?: string;
    onSave: (value: string) => void;
    value?: string;
}

export default class TodoTextInput extends React.Component<Props, {value: string;}> {

    constructor(props: Props) {
        super(props);
        this.state = { 'value': props.value ? props.value : '' };
    }

    render(): JSX.Element  {
        return (
            <input
                className={this.props.className}
                id={this.props.id}
                placeholder={this.props.placeholder}
                onBlur={() => {
                    this.save();
                }}
                onChange={(event: React.FormEvent) => {
                    this.onChange(event);
                }}
                onKeyDown={(event: React.KeyboardEvent) => {
                    this.onKeyDown(event);
                }}
                value={this.state.value}
                autoFocus={true}
            />
        );
    }

    private save(): void {
        this.props.onSave(this.state.value);
        this.setState({ 'value': '' });
    }

    private onChange(event: React.FormEvent): void {
        this.setState({ 'value': event.target['value'] });
    }

    private onKeyDown(event: React.KeyboardEvent): void {
        if (event.keyCode === ENTER_KEY_CODE) {
            this.save();
        }
    }

}

ちなみに

単に TypeScript の JSX/React サポートを試すだけなら、 component だけ書いておけばよかったわけですが、なぜ Flux 一式書いてみたかというと、 ts-eventemitter という自作ライブラリを試してみたかったからです。

それについては別途記事を書く予定です。 -> 書きました TypeScript の機能で EventEmitter のイベントの payload を型安全に扱う