React + TypeScript でフラグ分岐するpropsを型安全に作る

## 前書き
ちょっとしたストーリーです。
不要な方は [Conditional Typesを適用してみよう](#conditional-typesを適用してみよう) まで飛んでね。

### ある日のでき事です。
「このコンポーネント、フッターなしで使いたいんですが、フラグで分岐していいですか？」
そんなissueが飛んできました。
私もちょうど同じ様な使い方がしたかったので、「是非お願いしますー」と返事をしました。

### before/after
before

```tsx
const Table: React.FC<{
    count: number;
}> = (props) => (
    <div>
        {props.children}
        <Footer count={props.count} />
    </div>
);
```

after

```tsx
const Table: React.FC<{
    count?: number;
    withFooter?: boolean;
}> = (props) => (
    <div>
        {props.children}
        {props.withFooter !== false
            ? <Footer count={props.count} />
            : null
        }
    </div>
);
```

既存の利用箇所を考慮して、「デフォルトではフッターありで、`withFooter===false`を指定するとなしにできる」と拡張されました。

### ちょっとした問題
やってもらえてラッキー、だったんですがちょっとした問題ができました。
`withFooter=true`の時は`count`は必須で欲しいのですが、
`withFooter=false`の時は不要なので、両方を満たすために`withFooter?:`と任意項目になっています。
これでは`withFooter=true`の時に`count`を渡し忘れてもrenderしてみるまで気がつけないのです。

## 型安全にしてみよう
### 問題点の整理
- プロパティの`count`は、同じくプロパティの`withFooter`が`true`なら必須、`false`なら不要なのに、両方を満たすために`任意`とされている
- 上記から、型としては`true+countなし`または`false+countあり`が許されるが、前者は動かず、後者は無駄
- `Footer`コンポーネントを分離すれば良いという考えもあるが、書ききれない背景を考慮するとコストが見合わなかった

### Conditional Types
そこで使いたいのが Conditional Typesです。
これは簡単に言うと、条件によって分岐する型です。（そのまま）
三項演算子に似た形で書き、extendsの左が右に置き換えられるならtrue相当の動きをします。
下記の場合は`Dog`は`Animal`に置き換え可能で`Foo: number`となります。

```ts
interface Dog extends Animal
type Foo = Dog extends Animal ? number : string;
```

### Conditional Typesを適用してみよう
[全文はこちら](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wG4AocmATzCTgHVgYALAMQghiSjgF4CAd2btO3KGSq16TVhACuMDlx78hIhUrE9JNOoxHLxABRxgAzmuToYAOlMQLslgGEWwADYATIgDsAPADe5HChcMKsRjwAXHCYKB7mSAA05AC+AHwUejIailFQDhZWxHZF5s5unj5IAcFh4YbaUAD8sTBQ8ikhYWgKvjCxvvIgAEY8FJnZ0nAAKiijHkjl-rNqzgUZamtIAB7cvl6WzpoFPaEtBpHN5edwsSf5N2bmFOR9vubwwBVNKrwCAAUYBeDz+JheAB9Hlp-uUAJSxEGOSw-OYLJYrDbNLZ8LbIiy2CKif5wACEfAE8USSFIcEoHy+cAKsWsGFsbBcQTgfXkAyGI3GvEyamBL3h-C2-i8wAAbhlAgTzLZeQM0v4APQy+VvPaQWA8iCfeDzRb0ASrZJwDJilGxU2Yl6rDKIxClWxsmAAOQgXnNW0Bd2lcoydwaipeKvc3j8aTDYUCZJ+2P+tos8PjDQucGGHg8mazsX8BUNfJgfAjKJV-RgaTgGtDWbCcYamu1ofhpCAA)

#### 1.props型を作る
事前準備で、分岐用専用の型として`WithFooter`,`WithoutFooter`を作りました。

```ts
type WithFooter = 'withFooter';
type WithoutFooter = 'withoutFooter';
```

そしてこちらがメインです。
`T`型に`WithoutFooter`を指定すると`WithoutFooterProps`になり、それ以外（指定なしを含む）では`WithFooterProps`になります。
`withFooter`が`boolean`ではなく、`?: true` `false` となっているのもポイントです。

```ts
type WithFooterProps = React.PropsWithChildren<{
    withFooter?: true,
    count: number;
}>;
type WithoutFooterProps = React.PropsWithChildren<{
    withFooter: false,
}>;
type TableProps<T = WithFooter> = T extends WithoutFooter
    ? WithoutFooterProps
    : WithFooterProps;
```

#### 2.コンポーネントに適用
genericsが必要になりますが、`React.FC`では上手く書けなかったので分解しています。
また、`<T, >`とは、「これはjsxではなくてgenericsだよ」、とパーサーにわかってもらうための小技です。

```tsx
const Table = <T, >(props: TableProps<T>): React.ReactNode => (
```

Type Guardも作って適用しましょう。

```ts
const isWithFooter = (props: WithFooterProps|WithoutFooterProps): props is TableProps<WithFooter> => props.withFooter !== false; 
```
```tsx
{isWithFooter(props)
    ? <Footer count={props.count} />
    : null
}
```

#### 3.利用箇所に適用
```tsx
// フッターありの場合
<Table count={count} />
```
```tsx
// フッターなしの場合
<Table<WithoutFooter> withFooter={false} />
```

## まとめ
無事に型安全にpropsの分岐を作ることができました。
もちろんコンポーネント自体をリファクタリングした方が良いというのもありますが、
選択肢の一つとして持っておけると、いざという時に役に立つかと思います。

## 参考リンク
- [conditional-types(公式)](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html)
- [TypeScript 2.8 の Conditional Types について](https://qiita.com/Quramy/items/b45711789605ef9f96de)