6歳娘「パパ、型による条件分岐はできないの？」

# とある休日
娘「ねぇ、パパ！」
娘「`switch`やろ〜！」

ワイ「おお、ええで！娘ちゃん！」
ワイ「**Switch**やろう！」
ワイ「ほな、テレビをつけて・・・」

娘「テレビ？」
娘「何を言っているの、パパ？」
娘「TypeScriptの`switch`文のことだよ？」

ワイ「ファッ！？」

# `switch`文で何をしたいのか

娘「今ね、ショッピングサイトを構築してるところなの」

ワイ「ほうほう」

娘「それでね、手広く儲けようと思って」

ワイ「おお、ええやんか」

娘「個人ユーザーだけじゃなく、法人ユーザーも登録できるようにしようと思うの」

ワイ「なるほどな」

娘「言語は**TypeScript**を使っているんだけど」
娘「ちょっと聞きたいことがあるの」

ワイ「おう、なんでも聞いてや」

# あいさつ関数を作っている

娘「ショッピングサイトにログインしたときに・・・」

> 個人の場合 → 「無職 やめ太郎さん、こんにちは！」
> 法人の場合 → 「株式会社無職さん、こんにちは！」

娘「↑こんなメッセージを表示させたいから」
娘「そのための**あいさつ関数**を作っているの」

ワイ「なるほどな」

娘「それでね」

# 型によって条件分岐させたい

娘「例えば、**Elm**っていう関数型言語なんかだと・・・」

```elm:Elm
-- greetingMessage関数は、ユーザーを受け取って、あいさつの文字列を返す

greetingMessage : User -> String
greetingMessage user =
    -- case式で処理を分岐
    case user of
        -- 個人の場合
        PersonalUser { familyName, givenName } ->
            familyName ++ " " ++ givenName ++ "さん、こんにちは！"

        -- 法人の場合
        CorporateUser { name } ->
            name ++ "さん、こんにちは！"
```

娘「↑こんな感じで」
娘「ユーザーが`PersonalUser`なのか`CorporateUser`なのかによって」
娘「処理を分岐できるじゃない？」

ワイ「そういえば、そうやったね」
ワイ「**代数的データ型**を使った、**パターンマッチ**いうやつやね」

娘「そんな風に、TypeScriptで**型による分岐**[^1]はできないの？」
娘「とりあえず、こんなカスタム型を定義してみたんだけど」

```diff_typescript:TypeScript
    // ユーザーには「個人ユーザー」と「法人ユーザー」がいまっせ！
    type User = PersonalUser | CorporateUser

    // 個人ユーザーは「姓」と「名」を持ってまっせ！
    type PersonalUser = {
      familyName: string
      givenName : string
    }

    // 法人ユーザーは名前だけを持ってまっせ！
    type CorporateUser = {
      name: string
    }
```

ワイ「なるほどな」
ワイ「しかし残念ながら」
ワイ「Elmみたいな**代数的データ型**の機能は」
ワイ「TypeScriptには無いし・・・」

- `PersonalUser`型だった場合の処理
- `CorporateUser`型だった場合の処理

ワイ「↑みたいな、型による条件分岐もできひんのや・・・」

# TypeScriptの型は、実行時には存在しない

ワイ「なぜなら、TypeScriptで書いた型情報は、コンパイル時にコード上から消されてしまうんや」

娘「あ、そっか」
娘「コンパイルされると、普通のJavaScriptのコードになるんだもんね」

ワイ「せや」
ワイ「だから、TypeScriptで定義したカスタム型の情報を」
ワイ「`if`文や`switch`文の条件に使うことはできひんのや」


娘「そうだよね・・・」

ワイ「できることと言えば・・・」

```diff_typescript:TypeScript
  if ("familyName" in user) {
    // 個人の場合
    return `${user.familyName} ${user.givenName}さん、こんにちは！`
  } else {
    // 法人の場合
    return `${user.name}さん、こんにちは！`
  }
```

ワイ「↑こんな感じで」

> `familyName`というプロパティを持っていたら個人ユーザー」

ワイ「とか、そんな感じで分岐させるしかないな」

娘「そっか・・・」
娘「せっかく型駆動で開発してるんだから」
娘「型によって分岐したかったな・・・」

# しかし娘ちゃん、思い出した

娘「あっ！そういえば・・・！」
娘「パパ、**あの絵本**を見せて！」
娘「あの、**ブルーベリーの絵本**！」

ワイ「おお、[**ブルーベリー本**](https://gihyo.jp/book/2022/978-4-297-12747-3)のことか」

<img width="400" alt="スクリーンショット" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/849107ad-6c51-113b-6b5d-257b626a384c.png">

ワイ「**絵本ちゃうけどな**」

# ブルーベリー本に書いてあった

娘「パパ、やっぱりできるよ！」
娘「TypeScriptでも、**代数的データ型**みたいなことができるよ」
娘「擬似的にだけど」

ワイ「おお、そうなんか」

娘「うん」
娘「さっきの型に・・・」

```diff_typescript:TypeScript
    // ユーザーには「個人ユーザー」と「法人ユーザー」がいまっせ！
    type User = PersonalUser | CorporateUser

    // 個人ユーザーは「姓」と「名」を持ってまっせ！
    type PersonalUser = {
+     type: "PersonalUser"
      familyName: string
      givenName : string
    }

    // 法人ユーザーは名前だけを持ってまっせ！
    type CorporateUser = {
+     type: "CorporateUser"
      name: string
    }
```

娘「↑こう、プロパティとして**型名**を持たせてあげるの」
娘「値に**タグ**をつけてあげる感じね」
娘「そうすることで・・・」


```diff_typescript:TypeScript
// greetingMessage関数は、ユーザーを受け取って、文字列を返す
const greetingMessage = (user: User): string => {
  switch (user.type) {
    // 個人の場合
    case "PersonalUser":
      return `${user.familyName} ${user.givenName}さん、こんにちは！`

    // 法人の場合
    case "CorporateUser":
      return `${user.name}さん、こんにちは！`
  }
}
```

娘「↑こう、型名による分岐ができるの」

ワイ「おお、良さげやな」
ワイ「`Elm`のパターンマッチにそっくりやな」
ワイ「しかも・・・」

**個人の場合**
![スクリーンショット 2022-05-21 15.46.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/5ad698f6-2f4d-896f-f069-90219afb282b.png)
↑`user`は`PersonalUser`型になっている

---

**法人の場合**
![スクリーンショット 2022-05-21 15.46.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/2bf92e26-6fb7-456f-730d-d804ba99ba36.png)
↑`user`は`CorporateUser`型になっている

---

ワイ「ちゃんと**型の絞り込み**もされとるやないかい」

娘「うん、だから例えば」
娘「法人なのに、間違って`familyName`にアクセスしようとすると・・・」

![スクリーンショット 2022-05-21 16.24.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/fc074473-8385-979e-8847-927637367216.png)

娘「↑ちゃんとコンパイルエラーが表示されて」
娘「ミスが起こらないようになってるよ」

ワイ「おお、ほんまやな」
ワイ「でも、**型による分岐**と言うと語弊があるかもな」
ワイ「**値の構造によって**分岐しとるわけやからな」

娘「そうね」

# まとめ
- TypeScriptでも、擬似的に**代数的データ型**を再現できる
  - オブジェクト型を定義するときに、プロパティとして型名を持たせておく
  - パターンマッチ的なこともできる

ワイ「↑ってことやな」

娘「うん」

ワイ「こう、型名で分岐できると、なんかエエ感じやな」

娘「そうだね」
娘「思考に近い感じでコードが書けて、いい感じだね！」

**〜おしまい〜**

# 参考文献

- [プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで](https://gihyo.jp/book/2022/978-4-297-12747-3)

いい本なので、よかったら買って読んでみてくださいやで。

# 新しい記事もよろしくやで
- [WHERE句を忘れただけなのに](https://qiita.com/Yametaro/items/c592460034b0e5bc131a)
- [6歳娘「パパ、条件によって変わる型を作りたいの」](https://qiita.com/Yametaro/items/9b2f0ab2037450004816)

# Zennにも記事を書いてみましたやで
- [独り言形式で綴る「OGP & SSR」](https://zenn.dev/tokumoto/articles/0098b3e850676f)

# noteも書いてますやで

- [ワイ「三角関数って、要らなくね？」](https://note.com/yametaro/n/n4ad2f07cde72)

[^1]: Elmのコードにある`PersonalUser`や`CorporateUser`は、**型**ではなく**バリアント**と呼ばれるもので、**型によって分岐**している訳ではありません。値の構造によって分岐しています。