TypeScriptでReactを書いてみて困ったところ

最近angularのためにtsの勉強をしていたので、いきなりangularをやるより慣れ親しんだreactでかるくかいてみようということで。

そしたら、僕の知っているreactではないreactもいたので困ったところをまとめていきたいと思います。

# React.Componentを継承するにはジェネリクスを使う必要がある
ほうほう、ジェネリクスは勉強したぞってことでここはそんなに困らなかった、、と思いきや、propsがないときはどうしたらいいんだろうとなりました。

```js
class Todo extends React.Component<{}, TodoState> {
}
```
`{}`を渡してあげればいいだけでした。簡単。`TodoState`でinterfaceを渡してやるとあとはいい感じにしくれます。

# constructorには引数が必須
```js
constructor(props: any) {
  super(props);
  ...
}
```
普通にreactを書くときはsuperに引数はあってもなくも問題ありませんでしたが、tsでは必須のようです。
今回はpropsがないので`any`にしてありますが、本来は`TodoProps`に当たるinterfaceで型を指定するのがいいでしょう。

# inputの値が取得できない
reactならおきまりの`e.target.value`ですが、これを書いてみたところうまく取得できませんでした。
というかそもそも、eの型ってなんやねんってなり調べてみると、、

`React.FormEvent<HTMLInputElement>`型みたいです。なので、

```js
private handleChnage(e: React.FormEvent<HTMLInputElement>) {
  this.setState({ value: e.currentTarget.value });
}
```
ってしてやると、`e.target.value`と同じ挙動になりました。ジェネリクスで`HTMLInputElement`を指定してやらないとこれまたコンパイルエラーを吐かれます。

# 動的なObjectのkeyを設定できない（やり方がややこしいっぽい）

```js

private handleChnage(e: React.FormEvent<HTMLInputElement>) {
  this.setState({ [e.currentTarget.value]: e.currentTarget.value });
}
```
としたいところですが、これだとエラーを吐かれます。うまいことやればいけるようですが、それならこのままでいいかなってことで今回は変更してません。

# jsxの型は何？
reactといえばjsxで書くのが一般的ですよね。これを変数に、、と思ったところで、はて、これの型は一体？となりました。
調べてみると、`JSX.Element`だそうです。思ったより普通w

例えば、jsxの配列を返す関数は以下のように宣言できます。

```js
private todoList(): JSX.Element[] {
  return this.state.todos.map(t => <li key={t.id}>{t.content}</li>);
}
```
シンプルです。笑

# 分割代入における型宣言
reactでは`this.state...`とか`this.props...`とか長ったるいのが多いので、分割代入で、

```js
const { todos, value } = this.state;
```
とすることが多いです。では、このときどうやって型宣言をするんでしょう。

調べてみると、

```js
const { todos, value }: TodoState = this.state; // TodoStateはinterface
```
としたらいいみたいです。

interfaceを使わずにもかけるんですが、

```js
const { todos, value }: { todos: ITodo[]; value: string } = this.state;
```

と長くなってしまうのでよくありません。（そもそもこのコードでもinterfaceが使われている。）
なのでinterfaceを使ってスマートに書くのがいいでしょう。

そもそも、型推論に任せてしまってもいい気はしますが、、笑

# classとinterfaceで名前が衝突しないように注意！

```js
interface TodoState {
  todos: Todo[];
  value: string;
}

interface Todo {
  id: number;
  content: string;
}

class Todo extends React.Component<{}, TodoState> {
  ...
}
```
最初このように書いていたのですが、これだとTodoが競合してどこかでおかしくなります。
僕の場合は、

```js
const { todos, value }: TodoState = this.state;
const todo: Todo = {
  id: todos.length,
  content: value
};
```
としたところ、Todoクラスに必要なメソッドがないよと言われて？？？となりました。コンパイラも競合してることを教えてくれたらいいのに、、

```js
interface TodoState {
  todos: ITodo[];
  value: string;
}

interface ITodo {
  id: number;
  content: string;
}
```
のように、名前を変えることで解決しました。
通りで他のプロジェクトとかをみていると接頭辞のIが付いていたのか、、w
Iをつけるかnamespaceを分けるといいのかも。

# Todoアプリのサンプル
最後に僕が作ったTodoアプリのサンプルを掲載しておきます。

```js
import * as React from "react";

interface TodoState {
  todos: ITodo[];
  value: string;
}

interface ITodo {
  // ここをTodoにするとコンンポーネントと名前が衝突する。
  id: number;
  content: string;
}

class Todo extends React.Component<{}, TodoState> {
  constructor(props: any) {
    super(props);
    this.state = { todos: [], value: "" };
  }

  private handleChnage(e: React.FormEvent<HTMLInputElement>) {
    this.setState({ value: e.currentTarget.value });
  }

  private addTodo() {
    const { todos, value }: TodoState = this.state;
    const todo: ITodo = {
      id: todos.length,
      content: value
    };
    this.setState({ todos: todos.concat(todo), value: "" });
  }

  private todoList(): JSX.Element[] {
    return this.state.todos.map(t => <li key={t.id}>{t.content}</li>);
  }

  render() {
    return (
      <div>
        <input
          type="text"
          name="value"
          value={this.state.value}
          onChange={e => this.handleChnage(e)}
        />
        <button onClick={() => this.addTodo()}>add</button>
        <ul>{this.todoList()}</ul>
      </div>
    );
  }
}

export default Todo;
```