5歳娘「パパ、型ガードって何？」

## とある休日

娘（**5歳**）「ねえパパ」

ワイ「なんや？娘ちゃん」

娘「あのね」
娘「**TypeScript**を勉強してたら、**型ガード**っていう概念が出てきたんだけど」
娘「**型ガード**って何？」

ワイ「ああ、それはな」
ワイ「片面がクッキーで、もう片面がチョコのやつや」
ワイ「ほんで、チョコの面は**船の絵**みたいになってんねや」

娘「食べてみた〜い」

よめ太郎「それ**アルフォート**やないかい」

ワイ「Oh...よめ太郎...」

よめ太郎「そうやなくて」
よめ太郎「**型ガード**の話をしてんねん」
よめ太郎「変われ、わしが説明する」

## 選手交代

よめ太郎「そんで娘ちゃん」
よめ太郎「具体的にどういうタイミングで**型ガード**が気になったん？」

娘「ええとね」
娘「実は今、遊園地の**オンライン申し込みシステム**を作っててね」
娘「その遊園地では、12歳以下の子供は**料金が半額**になるから」
娘「あるユーザーが**12歳以下かどうか**を判定する関数を書こうとしてたの」

> 関数名： isChild
>
> - ユーザーが**子供料金**の対象かどうかを判定する関数
> - ユーザーが**12歳以下**であれば`true`を返す
> - ユーザーが**13歳以上**であれば`false`を返す

娘「↑こんな関数が必要なの」

よめ太郎「なるほどな」

娘「それでね」
娘「**年齢を登録していない**ユーザーもいるから」
娘「ユーザーを表す型は↓こんな感じで書いたの」

```typescript
type User = {
    name: string
    age: number | null
}
```

よめ太郎「なるほどな」
よめ太郎「ユーザーの**年齢**は`null`の場合もあるってことやな」

娘「うん」
娘「それで、さっきの**子供判定関数**を書こうとしてみたの」

```typescript
const isChild = (user: User): boolean => {
    return user.age <= 12
    // -> true または false
}
```

よめ太郎「なるほどな」
よめ太郎「引数としてユーザーを受け取って」
よめ太郎「**12歳以下かどうか**を真偽値で返すんやな」

娘「うん」
娘「でも、ここでTypeScriptの**エラー**が出ちゃったの[^1]」

![スクリーンショット 2020-11-19 10.56.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/7e1c59f8-00cd-410c-9e37-d4746944c58a.png)

よめ太郎「なるほどな」
よめ太郎「`null`かもしれん値を、**数値と比較**しようとしたからやな」

娘「調べてみたところ」
娘「こういうときには**型ガード**が必要みたいなんだけど・・・」

ワイ「ちゃうちゃう」
ワイ「こういうときに必要なのは、**型アサーション**や」

## 型アサーションしてみる

ワイ「`user.age`を**数値として扱う**ために」
ワイ「`user.age as number`って書けばええんや」

娘「なるほどね！」

> 「`user.age`は、ここでは`number`型やで！」

娘「↑こんな風に**主張**するんだね」
娘「**アサーション**って、**主張**とか**断言**って意味だもんね！」
娘「やってみるね！」

![スクリーンショット 2020-11-19 11.09.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/339f233c-0eb1-55d5-2d84-8b446dce15fd.png)

娘「あっ！ちゃんとエラーが消えた！」
娘「パパ、ありがとう！」

ワイ「ゲヘヘ」

よめ太郎「**いや全然あかんで**」

ワイ「なんでや」

よめ太郎「試しに、今の状態の`isChild`関数を実行してみい」

ワイ「ぐぬぬ、やってみたるわ」


```typescript
const ojisan: User = {
    name: 'やめ太郎',
    age: null
}
```

ワイ「↑まずは、こうやな」
ワイ「試しに1人ユーザーを作ってやるんや」
ワイ「**年齢は登録していない**パターンにしといたで」
ワイ「ほんで、次は・・・」

```typescript
console.log(isChild(ojisan))
```

ワイ「↑こうや！」
ワイ「`isChild`関数で、`ojisan`が子供かどうかチェックしたるんや！」
ワイ「結果は・・・！」

> **true**

ワイ「**ファッ！？**」
ワイ「`true`！？」
ワイ「`ojisan`が**子供扱い**になってもうた・・・」

よめ太郎「そうやで」
よめ太郎「`null`と`12`を比較したら」
よめ太郎「`null`の方が**小さい**ことになってしまうねん」

ワイ「マジか」
ワイ「実際には**年齢を登録してないだけ**なのに」
ワイ「**12歳以下**ってことになってまうやんけ・・・」

よめ太郎「せやから`null`と数値の比較とか、せんほうがええで」
よめ太郎「そのために、わざわざコンパイラくんが・・・」

> オブジェクトは 'null' である可能性があります。

よめ太郎「↑こんなエラーを出してくれてたんや」

ワイ「なるほどな・・・」

よめ太郎「`null`の可能性もあるのに」
よめ太郎「無理やり**型アサーション**でコンパイラに言うことを聞かせて」
よめ太郎「数値扱いなんてしたらあかんねん」

ワイ「ぐぬぬ・・・」

よめ太郎「そこで、**型ガード**や」

## 型ガードしてみる

よめ太郎「こうや！」

```typescript
const isChild = (user: User): boolean => {
    if (user.age === null) return false // <- 追加

    return user.age <= 12 // <- 型アサーションはしない
}
```

ワイ「ほうほう・・・」
ワイ「年齢が`null`の場合には、すぐに`return false`して」
ワイ「**子供じゃない**扱いにしたるんやね」

よめ太郎「せや」
よめ太郎「`user.age`が`null`、つまり**年齢が未登録**の場合は」
よめ太郎「子供料金を適用したらあかんからな」

ワイ「せやな」

よめ太郎「こうすることで、その下の行の`user.age`は」
よめ太郎「`null`である可能性が**排除される**から・・・」

```typescript
   return user.age <= 12
```

よめ太郎「↑この部分の型エラーは出なくなるんや」
よめ太郎「数値が来て欲しいところに`null`が来てしまう可能性を排除できたんや」

ワイ「ほんまや」

> オブジェクトは 'null' である可能性があります。

ワイ「↑このエラーが表示されんくなっとるな」
ワイ「**型アサーション**も消したのに」
ワイ「ちゃんと`user.age`が数値として認識されとるみたいやな」


娘「そっか」
娘「年齢が`null`だった場合には**早期リターン**されちゃうから」
娘「その先の行まで**たどり着けない**はずだもんね」
娘「この行では`user.age`は必ず`number`型だ、ってことが保証[^2]されたから」
娘「エラーが出なくなったんだね！」

```typescript
const isChild = (user: User): boolean => {
    // user.age が null なら、次の行で処理は終了する 
    if (user.age === null) return false

    // 従って、ここでは user.age は null になる可能性はなく
    // number となる
    return user.age <= 12
}
```

娘「↑こういうことだね！」

よめ太郎「そういうことや」
よめ太郎「TypeScriptのコンパイラくんは」

> コンパイラ「`user.age`が`null`になる可能性は、関数の1行目で消えたな！」
> コンパイラ「ほな、次の行では`user.age`は確実に**数値**や！」

よめ太郎「ってことを理解してくれるんや」

ワイ「へえ〜、条件をつけて、**先の処理に進める型を絞り込む**わけか」
ワイ「でも、何でこれを**型ガード**って呼ぶんやろ・・・」

## なぜに型「ガード」か

よめ太郎「**guard**って単語は」
よめ太郎「番人、見張り、みたいな意味やから」

> 型の番人「`number`が来るべきところに`null`が入り込まないよう、見張っておきます！」
> 型の番人「きちんと**ガード**します！」

よめ太郎「↑こんなイメージや」

娘「そう言われてみると、まさに**型ガード**って感じだね」

ワイ「ほんまや」
ワイ「`number`とか`null`ってのはあくまで今回の例やけど」
ワイ「要するに・・・」

> 型の番人「この型は、通ってヨシ！」

ワイ「↑こんな感じやな」

## まとめ

- 来るべきでないところに`null`や`undefined`が来る可能性が残っていると、TypeScriptのコンパイラはエラーを出して教えてくれる
- そのとき、型アサーションで無理やりコンパイラに言うことを聞かせるのは、なるべく控えよう
- 型ガードで型を絞り込んで、**あるべき型の値だけが先の処理に進めるように**すれば、ちゃんとエラーは消せる

ワイ「ってことやで！娘ちゃん」

娘「うん、聞いてたから知ってる」

ワイ「それな」

**〜おしまい〜**

## おすすめ文献


- [**型ガード - TypeScript Deep Dive 日本語版**](https://typescript-jp.gitbook.io/deep-dive/type-system/typeguard)  
色んな型ガードの例が紹介されてるで！

## 新しい記事もよろしくやで！
[**Hurry Coderと魔法の数字**](https://qiita.com/Yametaro/items/87130f961af294f75d60)

[^1]: TSConfigで、`strict`または`strictNullChecks`を`true`にしていないとエラーになりません。ぜひ`true`にしておきましょう。
[^2]: any型を使ってると保証されないから、TSConfigで禁止しましょう。。。