食わず嫌いを克服したい：TypeScriptで苦手なところだけかいつまんでやってみた②

## はじめに
TypeScript、難しいですよね（少なくとも私は得意ではありません）？ 特に抽象度が高くなるとすぐに脳内メモリが足りなくなるので、なんとなくの実装で乗り切ってきたのですが...。そうこうも言ってられなくなったので（遅い）、曖昧なところを潰してみることにしました。

▼前回の記事はこちら
https://qiita.com/sazumy/private/7c4f35500aa51210a4d4

## 使用テキスト
使用したのはこちらのテキスト。コードもこちらの書籍から引用しています。全てのコードには該当ページ数も書いておきます。

https://www.amazon.co.jp/dp/4297127474


## ユニオン型

まずは**ユニオン型**です。ユニオン型は「TまたはU」という状態を表す方で、**T | U**と書きます。具体例はこんな感じです。`Animal`または`Human`の型を持つ`User`という方は、以下のような事例に使うことができます。

```ts
type Animal = {
  species: string;
}

type Human = {
  name: string;
}

type User = Animal | Human;

// User型はnameまたはspeciesのプロパティを持つので、これはコンパイルが通る
const tama: User = {
  species: "cats"
}

// これもコンパイルが通る
const firstUser: User = {
  name: "taro"
}

// titleというプロパティはUser型にないので、これはコンパイルエラー
const book: User = {
  title: "The Tale of Peter Rabbit"
}

//　これもコンパイルが通る？？？
const pochi: User = {
  species: "dogs",
  name: "pochi"
}
```
> プロを目指す人のためのTypeScript入門:p.248のコードを改変して使用

解せないのは、`species`と`name`の両方のプロパティを持つ、以下のオプジェクトもコンパイルが通ってしまうこと。

```ts
const pochi: User = {
  species: "dogs",
  name: "pochi"
}
```

**TかつU**という型を作るのは、この記事の下方で挙げている**インターセクション型**の役割じゃなかったっけ？
...結局この点については解消しなかったのですが、暫定的に**上記の時点では、`pochi`は`Animal`であるか`Human`であるか確定していないから**という結論になりました。誰か正しい答え知っていたら教えてください...

なお、ユニオン型は下記のように書くことも可能です。
```ts
type User =
    | Animal
    | Human
```
実務のコードで時々見ますが、そういう意味だったのかと改めて確認:relaxed:

## インターセクション型
**インターセクション型**は「TかつU」を表す型で、**T & U**と書きます。そして、**オブジェクト型を拡張する新しい型**を作ります。
具体例は以下のようになります。

```ts
type Animal = {
  species: string
}

type Human = {
  name: string
}

type User = Animal & Human;

const pochi: User = {
  species: "dogs",
  name: "taro"
}

// こんな風にオブジェクト型ベタ書きでも型を拡張できる
type AnimalWithAge = Animal & { age: number };

const tama: AnimalWithAge = {
  species: "dogs",
  age: 5
}
```
> プロを目指す人のためのTypeScript入門:p.253のコードを改変して使用

こちらはとても簡単ですね:relaxed:
個人的には `Animal & { age: number }`みたいに、**オブジェクトの型 & オブジェクトの型のベタ書き**で拡張できるのがよかったです。色々柔軟に考えられそう。

## ユニオン型とインターセクション型の練習
頭の体操的な記載も一つ載っていたので試してみることにしました。
このような、`Human`型のオブジェクトを受け取ったら`name`を、`Animal`型のオブジェクトを受け取ったら`species`を返すメソッドを作ってみました。

```ts
const tama: Animal = {
  species: "cats"
}
const firstUser: Human = {
  name: "taro"
}

const getName = (human: Human) => {
  return human.name;
}
const getSpecies = (animal: Animal) => {
  return animal.species;
}
// randomFuncは、const randomFunc: ((human: Human) => string) | ((animal: Animal) => string) というユニオン型
const randomFunc = Math.random() < 0.5 ? getName : getSpecies

// ここでコンパイルエラーになる
randomFunc(tama)
randomFunc(firstUser)
```
> プロを目指す人のためのTypeScript入門:p.255のコードに追記して使用

`randomFunc`は`((human: Human) => string) | ((animal: Animal) => string)`というユニオン型ですが、呼び出す際に`Human`型か`Animal`型のどちらかのオブジェクトを入れて呼び出すとエラーになります。

そこで、`tama`と`firstUser`に`Human & Animal`型である以下の型を適用。

```ts
// ここをHuman & Animal型に
const tama: Human & Animal = {
  name: "tama",
  species: "cats"
}
// ここもHuman & Animal型に
const firstUser: Human & Animal = {
  name: "taro",
  species: "human"
}

const getName = (human: Human) => {
  return human.name;
}
const getSpecies = (animal: Animal) => {
  return animal.species;
}

const randomFunc = Math.random() < 0.5 ? getName : getSpecies
// コンパイルエラーが出ない
randomFunc(tama)
randomFunc(firstUser)
```
> プロを目指す人のためのTypeScript入門:p.256のコードに追記して使用

無事、エラーなく呼び出せました:relaxed:

## 感想
TypeScript...今でも苦手意識強いのですが、一つ一つ読み解いていくことで何とか実務でも落ち着いて取り組めそうです。
アドベントカレンダーという機会があってやってみてよかったです。