kotarou1192

人間に擬態中

ReactチュートリアルをTypeScriptで書き下す

こんばんは、たかしです。

身内で勉強会をやっているのですが、そこでTypeScriptでReactを書こうという話になりました。
そこでReactチュートリアルをTypescriptで書き下してウォーミングアップをしようという話になったのですが、これがなかなか面倒くさそうだったのでここにまとめたいと思います。
どなたかの参考になれば嬉しいです。
環境構築は飛ばして早速本編に入っていきます。

データをprops経由で渡す

そもそもpropsってなんぞやということで調べました。

コンポーネントと props – React

propsとは
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

一言で表すならば上の通りです。 つまり、propsというオブジェクトを渡しているということですね。 これは次と等価だそうです。

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

しかしこれをTypeScriptで表そうとなると一つ問題があります。

propsの型がなんなのか分からない問題

話をReactチュートリアルに戻して、renderSquare関数でSquareコンポーネントのpropsのプロパティにvalueを定義し、そこに数字を渡して数字の書いてあるボタンを表示する部分があります。

こちらです。

チュートリアル:React の導入 – React

これをそのままタイプスクリプトでやろうとすると次のようなエラーが出るはずです。

Property 'value' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Square> & Readonly<{}> & Readonly<{ children?: ReactNode; }>'.

プロパティ「value」は型「intrinsicAttributes」と「IntrinsicClassAttributes」と「Readonly<{}>」と「Readonly<{ children?: ReactNode; }>」 の中に存在していません。みたいな。

つまり型が問題みたいです。

以下の方法で解決しました。

まず、propsの型を定義してあげます。

interface SquareProps {
  value: number;
}

次にこの作った型をクラス呼び出し時に適用されるようにしてあげます。

class Square extends React.Component<SquareProps> {
  render() {
    return (
      <button className="square">
        {this.props.value}
      </button>
    );
  }
}

ちなみにReact.Componentの受け取る型はこうです。

class React.Component<P = {}, S = {}, SS = any>

PはPropsのことだと思われます。上のコードでこのPに型が与えられました。
SはStateのことだと思われます。Stateとは、コンポーネントごとに持っている状態のようなものだそうです。
今回はまだ使っていないので定義しないでおきます。

以上の編集で実際に動作確認してみます。

board001

動きました、ヨシ!

では次に進みましょう。

インタラクティブコンポーネントを作る

詳細はこちらです。
チュートリアル:React の導入 – React

この章でついにStateをつかうのですが、ここでまた問題発生です。
素直に書いていると下のようなエラーが出るはずです。

Property 'value' does not exist on type 'Readonly<{}>'

今回も素直にこうしてあげましょう。

interface SquareState {
  value: string;
}
lass Square extends React.Component<SquareProps, SquareState> {
  constructor(props: SquareProps) {
    super(props);
    this.state = {
      value: "",
    };
  }
  render() {
    return (
      <button
        className="square"
        onClick={() => {
          this.setState({ value: "X" });
        }}
      >
        {this.state.value}
      </button>
    );
  }
}

これで動きます。

この後に関しては難しいところがなさそうなので書くかは謎です。