きゃりぱみゅの名曲にならえ！TypeScriptで書かれたFunctionComponentでchildrenPropを使う方法

**すみません、きゃりーぱみゅぱみゅは終盤まで出てきません🙇‍♂️**

## モチベーション🔥
ReactをTypeScriptで書いていたとき、
FunctionComponentでchildrenPropを使おうとしましたが、
書き方が結構ややこしかったので、**アンサー**と**何故そうなるのか**を書いておこうと思った次第です！

## TL; DR🍜
- FunctionComponentは、外部から渡される引数にたいして、childrenPropを勝手に `合成` していた
- 合成の理解には、まずTypeScriptの `交差型(Intersection Types)` を知る必要があった

## 環境🤖
この記事では `Reactv16.6` を使っています

## 記事に出てくるキーワード💬
### FunctionComponent
- 状態を持たない純粋関数コンポーネントのこと
- StatelessComponent とか StatelessFunctionalComponent とか呼び名がある。正式名称は`FunctionComponent`ぽい！

FunctionComponentとはこんなコンポーネントのことです。主に[PresentationalComponent](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0)として使われます。

```tsx
const Hello = () => <p>Hello</p>
```

FuntionComponentはパフォーマンスの文脈で語られることが多いですが、
個人的には、状態を管理する場所を出来るだけ減らしたい、ということで使っています。

余談ですが、最近RFCが公開された[Hooks](https://reactjs.org/docs/hooks-intro.html)もFunctionComponentをベースにしていますね。
（[Hooks.guide](https://www.hooks.guide/)でHooksの実装例が披露されていたりします。コミュニティが盛り上がっている感じあります）

### childrenProp

```tsx
<div>
    <p>Hello</p> /* このpタグはdivタグに childrenProp として渡される */
<div>
```

**詳しくは[この記事](https://qiita.com/kazumicho/items/4f862b618eda09e14ec7#children-prop%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)で説明しました**

## 本題✍️

そもそもこの記事のきっかけになったのは、適当にウェブメディアっぽいものを作っていたときです。

具体例がないとわかりづらいので、ここからはQiitaの記事ページを例にして説明します。
<img width="686" alt="スクリーンショット 2018-12-03 18.04.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/143848/abc8dbe8-b37a-a220-069a-b85de4623fd0.png">

記事ページのURLですが、 `https://qiita.com/${ユーザ名}/items/${記事Id}` となっていますよね。

記事一覧ページからではなく、URLから記事ページに直アクセスされたとき、
**URLに含まれる記事Idが不正だった（存在しない記事とか）場合、コンポーネントとしてどうハンドリングしようか**
と考えていたのが出発点でした。

---

では、以下のコンポーネント構成がある前提で話をすすめます。

- `App.tsx`: 
  - React.Componentを継承したコンポーネント
  - getItemメソッドを使うと、記事をローカルあるいはリモートサーバーから取得できる
 
- `ItemPage`: 
  - FunctionComponent
  - App.tsxから記事を受け取って、表示する

はじめは、適当に以下のように書いていました。（React-Routerを使っています）

```App.tsx

getItem = (id: string) => /* 記事を取得する処理 */

<BrowserRouter>
    <Switch>
    .
    . /* いろんなルーティング */
    .
        <Route
            exact
            path={"/items/:id"}
            render={({ match: { params }}: { match: { params: Object }}) => (
                <ItemPage item={getItem(params["id"])} />
            )}
        />
    </Switch>
</BrowserRouter>
```

今回の本題ではないのでReact-Routerの説明は省きますが、
このコードだと`params["id"]`にアクセスすることで記事Idを取得できます。

ItemPageコンポーネントの `item` プロパティに、getItemメソッドで取得した記事を渡すというイメージです。

**しかし**、

これだと、存在しない記事Idが渡ってきた時のハンドリングをItemPageコンポーネントに任せなければいけません。
つまり、ルーティングのミスをItemPageコンポーネントがカバーしなけれいけないという考え方になりますよね。

ItemPageに対して**「表示するものがない場合がある」という知識を持たせるのは微妙**ですね。

そこで、記事が存在するか確認して、
なければ404ページへと誘うラッパーコンポーネント（WithNotFound）をFunctionComponentとして定義しました。

ラッパーが引数にchildrenPropを受け取るため、以下のように記述してあげます。

```WithNotFound.tsx
export const WithNotFound = ({
  targetPath, /* 記事Idが渡ってくる */
  predicate,  /* 記事Idを引数に受け取り、存在するか確認する関数 */
  children    /* このコンポーネントの内側に書いたコンポーネントは、childrenにわたってくる */
} : {
  targetPath: string;
  predicate: (targetPath: string) => boolean;
  children: React.ReactNode; /* 型定義が必要なので、なんでも受け取れる型にしておく */
}) => (predicate(targetPath) ? <>{children}</> : <NotFound />);
```

App.tsxも書き換えます。ItemPageコンポーネントをラッパーで包んであげます。

```App.tsx
<BrowserRouter>
    <Switch>
    .
    . /* いろんなルーティング */
    .
        <Route
            exact
            path={"/items/:id"}
            render={({ match: { params }}: { match: { params: Object }}) => (
                <WithNotFound targetPath={params["id"]}
                    predicate={id => /* 記事Idを元に、記事があるか確認する処理 */}>

                    <ItemPage item={getItem(params["id"])} />

                </WithNotFound>
            )}
        />
    </Switch>
</BrowserRouter>
```

これで一旦完成です！**が**、自分でchildrenPropを宣言しているのがダサい気がしたんですよね...

React.Componentを継承したコンポーネントであれば `this.props.children`でchildrenPropにアクセスできるのに！！

**ここから本当の本題です！**

ちょっと調べてみたら、`React.FunctionComponent`という出来合いの型でコンポーネントを作れば以下のようにスマートにかけることがわかりました✨

```WithNotFound.tsx
export const WithNotFound: React.FunctionComponent<{
    targetPath: string;
    predicate: (targetPath: string) => boolean;
}> = ({
    children, /* FunctionComponentの中で実際に使いたいので、書く必要がある泣 */
    targetPath,
    predicate
}) => predicate(targetPath) ? <>{children}</> : <NotFound />;
```

**あれ、childrenの型定義を書いていないけど、引数のchildrenが使える...?
コンパイラにもオコられないぞ...?**

なんで？よくみると、React.ReactNodeという型が勝手についています。
![スクリーンショット 2018-12-04 2.59.21.png](https://qiita-image-store.s3.amazonaws.com/0/143848/cd402d15-5363-025e-06d6-ac4dfd29af7e.png)

`React.FunctionComponent`をみてみましょう。どうなっているのやら!

```index.d.ts
interface FunctionComponent<P = {}> {
    (props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;
    propTypes?: ValidationMap<P>;
    contextTypes?: ValidationMap<any>;
    defaultProps?: Partial<P>;
    displayName?: string;
}
```

`props: P & { children?: ReactNode }` に注目してください

どうやら、引数には `Pというジェネリクス` & `{ children?: ReactNode }というオブジェクト` を受け取るそうです。

**.**
**.**
**.**

え、 `&`ってなんて説明すればいいんですか！🤷‍♂️ 

Pはわかります。上のコード例でいうところの

```tsx
{
    targetPath: string;
    predicate: (targetPath: string) => boolean;
}
```
のことです。

ただ、、 **&**が...

で、調べてみたところ、&とは、TypeScriptの[交差型](https://www.typescriptlang.org/docs/handbook/advanced-types.html#intersection-types)のことだそうです。

---

説明するより例をみた方がわかりやすいので以下読んでみてください！

```tsx
interface Hoge {
    foo: string
}

interface Fuga {
    bar: number
}

const hogeHuga: Hoge & Fuga = {foo: "ふ〜", bar: 99}
```

つまり、**型を合成**することができるんですね😲

なかなか使いどころが思いつきませんが、サーバーサイドにリクエストを送る時、どうしてもフォームに追加しなきゃいけないパラメータがあるケースなんかは、交差型が便利かもしれません！

例えば、どのデバイスからリクエストが送られたかも教えなきゃいけないケースとか（あるかな...?笑）

```tsx
const person: Person = {name: "太郎"}

interface RequestInfo {
    device: string
}

const sendingPerson: Person & RequestInfo = {...person, ...{device: "iPhone"}}
```

例にもどります。`props: P & { children?: ReactNode }`に従うと、

```tsx
props: { 
    targetPath: string; 
    predicate: (targetPath: string) => boolean
} & {
    children?: ReactNode
}

// つまり以下になる ↓

props: { 
    targetPath: string; 
    predicate: (targetPath: string) => boolean; 
    children?: ReactNode
} 
```
となることがわかりますね🎵 
だから、childrenPropに型が勝手に与えられていたんですね〜

##　さいごに🤚

きゃりーぱみゅぱみゅの『最&高』はつまり、最と高を合成していたんですね。歌って踊れてTypeScriptもできる才女です。

早&速、TypeScriptをもっと勉強したいと思います💪