React × TypeScriptの基本的な型定義

## はじめに
記述が理解できないのは、基礎を知らないからだ。ということで、学習用に React × TypeScript開発での最低限抑えておきたい型定義について解説していきます。

## 1. 型のインポート
```jsx:通常のインポート
import React from "react"

const HelloComponent: React.VFC = () => {
  return <div>通常のインポート</div>
}
```
`import type { 特定の型 }` と書くと**型情報のみインポート**できます。
```jsx:型情報のみのインポート
import type { VFC } from "react"

const HelloComponent: VFC = () => {
  return <div>型情報のみインポート</div>
}
```
## 2. 関数コンポーネントの型定義
関数コンポーネントの型定義には`React.VFC` という型を用います。
VFC = Void Function Component
```jsx
import React from "react"

const HelloComponent: React.VFC = () => {
  return <div>関数コンポーネントの型定義</div>
}
```

## 3. propsの型定義
propsとして**どんな値を受け取ることができるのか**を定義しておくことで、予期せぬpropsが渡ってくることを防ぐことができます。

### 3-1. propsを受け取らない場合
```jsx:propsなしの型定義
import React from "react"

const TypeInference = () => {
  return <div>型推論のパターン</div>
}

const TypeAnnotation: React.VFC = () => {
  return <div>型注釈のパターン</div>
}
```
### 3-2. propsを受け取る場合

2パターンあります。
 - **propsに直接型注釈する**
 - **`React.VFC<P>` ジェネリック型として指定する**

```jsx:propsありの型定義
import React from "react"

// propsとして受け取る型の定義
interface Sample = {
  text: string
}

const TypeAnnotation = (props: Sample) => {
  return <div>型注釈 {props.text}</div>
}

const GenericType: React.VFC<Sample> = (props) => {
  return <div>ジェネリック型 {props.text}</div>
}


/* -- 呼び出す側 -- */
const Parent: React.VFC = () => {
  return (
    <div>
      {/* 結果は同じ */}
      <TypeAnnotation text="TypeScript" />
      <GenericType text="TypeScript" />
    </div>
  )
}
```

## 4. useStateの型定義

### 4-1. 型推論に任せる

```jsx
// 初期値から型が明確な場合は型推論に任せる
const [text, setText] = useState("")         // string型
const [count, setCount] = useState(0)        // number型
const [isShow, setIsShow] = useState(false)  // boolean型

// 配列の場合
const [foods, setFoods] = useState(["pan", "rice"]) 
const [numbers, setNumbers] = useState([1, 2, 3])   

// オブジェクトの場合 { name: string型, age: number型, isMarried: boolean型 } というオブジェクトの型がuserステートに自動で付く
const [user, setUser] = useState({ name: "akihiro", age: 27, isMarried: false})
```

### 4-2. useStateの型定義（プリミティブ値）

ステートとして保持する値が**配列やオブジェクトではない場合**です。
```jsx
// useStateのジェネリック型<T>に明示的に型を指定する
const [text, setText] = useState<string>("") 
const [count, setCount] = useState<number>(0)
const [isShow, setIsShow] = useState<boolean>(false)

// nullを含む場合は、合併型（Union Type）を用いて複数の型を指定する
const [count, setCount] = useState<number | null>(null) //number型またはnull型
```

### 4-3. useStateの型定義（配列）

ステートの値が**配列である場合**の型の指定方法です。
```jsx
// useStateのジェネリック型<T>に直接配列の型を指定する
const [foods, setFoods] = useState<string[]>([])
const [numbers, setNumbers] = useState<number[]>([])

// 文字列と数値など、複数の値の配列を受け取る場合は以下の様に型を指定する
const [fuga, setFuga] = useState<(string | number)[]>([])   // string型またはnumber型の値を受け取る配列
```

### 4-4. useStateの型定義（オブジェクト）

ステートの値が**オブジェクトである場合**の型の指定方法です。
```jsx
import React from "react"

// ステートが持つオブジェクトの構造を型定義
interface UserData = {
  id: number
  name: string
}

const StateSample: React.VFC = () => {
  const [user, setUser] = useState<UserData>({}) // 初期値が型の構造を満たしていないのでエラー
  const [user, setUser] = useState<UserData>({ id: 123, name: "akihiro" }) // OK

  return (
    <div>
      <h1>{user.id}</h1>
      <h1>{user.name}</h1>
    </div>
  )
}
```

### 4-5. useStateの型定義（オブジェクトの配列）

ステートの値が**オブジェクトの配列である場合**の型の指定方法です。
```jsx
import React from "react"

// ステートが持つオブジェクトの構造を型定義
interface Todo = {
  id: number
  body: string
}

const StateSample: React.VFC = () => {
  const [todos, setTodos] = useState<Todo[]>([{}]) // 初期値が型の構造を満たしていないのでエラー
  const [todos, setTodos] = useState<Todo[]>([])   // 空配列はOK
  const [todos, setTodos] = useState<Todo[]>([{ id: 1, body: "OK" }])

  return (
    <ul>
      {todos.map((item: Todo) => {
        return <li key={item.id}>{item.body}</li>
      })}
    </ul>
  )
}
```

## 5. イベントオブジェクト（event）の型定義
`onClick` `onChange` などのイベントハンドラを用いて、イベントオブジェクト`event` を扱う際の型定義。
```jsx
// `handleChange`などの関数を定義する際のイベントオブジェクトの型定義
const [inputText, setInputText] = useState("")
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  setInputText(event.target.value)
}

// propsとして`onClick`などのイベントハンドラを子コンポーネント側で受け取る時の型定義
interface Props = {
  handleClick: (event: React.MouseEvent<HTMLInputElement>) => void    // onClick
  handleChange: (event: React.ChangeEvent<HTMLInputElement>) => void  // onChange
  handleSubmit: (event: React.FormEvent<HTMLFormElement>) => void     // onSubmit
}
```

### 参考
https://zenn.dev/ogakuzuko/articles/react-typescript-for-beginner