【TypeScript】バックエンドをかじっている私は、「部分型」は「継承」と同じ考え方と認識するとしっくりきた

# 1.はじめに

本記事ですが、@kazatsuyu 様よりご指摘とアドバイス修正いたしました。  
サンプルコードの例（命名）が良くなかったり、  
部分型を上位互換と混乱を招く表現をしているとご丁寧にコメントを頂きました。  

今回その修正の記事となります。  

@kazatsuyu 様、ありがとうございました。

以前の記事を読んだ下さった方、混乱を招く記事を登校してしまい、申し訳ありませんでした。  
こちらの記事も読んでいただけますと幸いです。


まだ、間違って解釈している所ありましたら、ご指摘いただけますと幸いです。

---

部分型関係は、TypeScript を理解する上で重要な概念とされています。

ただ、

「どっちがどっちの部分？」  
「なぜ 〇〇 型が ◇◇ 型の部分型なら、 〇〇 型の値が ◇◇ 型の値を使える？」

となったので、部分型の概要を抑えるために「 [書籍：プロを目指す人のためのTypeScript入門](https://twitter.com/uhyo_/status/1517158176365641729?s=20&t=5jZBSZVbLrAsgkCUruO7Ig) 」を参考に記事を書きました。 

頭が柔らかくない僕は、理解するのに苦しみました。  
というか、間違って解釈していました…

同じ境遇をお持ちの方は見ていただけますと幸いです。


# 2.目次
[1. はじめに](#1はじめに)  
[2. 目次](#2目次)  
[3. この記事でわかること](#3この記事でわかること)  
[4. 環境](#4環境)  
[5. 部分型](#5部分型)  
　[5.1. 部分型とは？](#51部分型とは)  
　[5.2. SuperType にオプショナルなプロパティを追加する](#52supertype-にオプショナルなプロパティを追加する)  
　[5.3. SuperType を SubType に代入してみると](#53supertype-を-subtype-に代入してみると)  
　[5.4. Engineer 型にプロパティを追加](#54engineer-型にプロパティを追加)  
　[5.5. 少し複雑な部分型](#55少し複雑な部分型)  
[6. おわりに](#6おわりに)  
[7. 参考](#7参考)  



# 3.この記事でわかること

:::note info

- 部分型の特徴
- 部分型とは、継承と同じ考え方

:::

# 4.環境

- TypeScript: 4.7.4
- Node.js: 16.15.1



# 5.部分型

では、本題の **「部分型」は「継承」と同じ考え方** について触れていきます。  


## 5.1.部分型とは？

> 2つの型の互換性を表す概念です。型 S が 型 T の部分型であるとは、S 型の値が T 型の値でもあることを指します。

[書籍：プロを目指す人のためのTypeScript入門　鈴木僚太［著］](https://twitter.com/uhyo_/status/1517158176365641729?s=20&t=5jZBSZVbLrAsgkCUruO7Ig)  

著者も書籍に書いていますが、  
この言葉を見ただけでは、理解ができませんでした。

この **部分型** について、  
下記の部分型サンプルコードのエンジニア（Engineer）とフロントエンドエンジニア（FrontendEngineer）を例にして見ていきたいと思います。

```ts:
type Engineer = {
    name: string;
    year: number;
}

type FrontendEngineer = {
    name: string;
    year: number;
    frontendSkill: Array<string>;
}

const kakedashiFront: FrontendEngineer = {
    name: 'daishi',
    year: 1,
    frontendSkill: ['JavaScript']
};

const kakedashiEngineer: Engineer = kakedashiFront;

/**
 * 実行結果
 * { name: 'daishi', year: 1, frontendSkill: [ 'JavaScript' ] }
 */
console.log(kakedashiEngineer);
```


ここでは概要を掴むため、`Engineer` 型に `frontendSkill` プロパティなどの スキルを記述していません。  
違和感を感じるかもしれませんが、後ほど追加した内容でも説明しますのでご了承ください。  
いい例が思い浮かばず申し訳ありません。  

前置きはこれぐらいにして、本題に入っていきます。

このコードはコンパイルエラーになりません。  
これは、`FrontendEngineer` 型 の値である `kakedashiFront` が `Engineer` 型の特徴を持っているからです。

では、どういうことかそれぞれの型を見ていきます。

```ts:
type Engineer = {
    name: string;
    year: number;
}

type FrontendEngineer = {
    name: string;
    year: number;
    frontendSkill: Array<string>;
}
```

`Engineer` 型は、  
  - `name` プロパティは、`string` 型
  - `year` プロパティは、`number` 型

のオブジェクトの型です。

`Engineer` 型では、`name` と `year` 以外を定義していません。  
なので、`name` と `year` 以外が **あってもなくても** 問題ありません。

だけど、**ないかも** しれへんプロパティにアクセスするとコンパイルエラーになります。

次に、`FrontendEngineer` 型は、  
  - `name` プロパティは、`string` 型
  - `year` プロパティは、`number` 型
  - `frontendSkill` プロパティは、`string` 型の配列

のオブジェクトの型です。

:::note warn

`FrontEngineer` 型は `Engineer` 型の条件を満たしています。

:::

この型の **条件を満たしている関係** が、部分型関係となります。

`Engineer` 型は **上位の型（SuperType）**、`FrontendEngineer` 型は **下位の型（SubType）** という位置づけになります。  
（このように考えるといいよと、@kazatsuyu 様よりアドバイス頂きました。ありがとうございます m(_ _)m ）

これは、バックエンドをかじっている私には、**継承** の SuperClass、SubClass と同じ考え方なんだなと思い、しっくりきました。

継承とは、SuperClass に全体の性質を、SubClass には固有の性質を定義します。  
SuperClass の性質を SubClass に引き継いで、新たに性質を付け足したり上書きしたりができる概念のことです。  

 
なので、  

::: note info

- **上位の型（SuperType）** の `Enigineer` 型の性質（型とプロパティ）を引き継いだ **下位の型（SubType）** の `FrontendEngineer` 型は持つ  
- **下位の型（SubType）** の `FrontendEngineer` 型に新たにプロパティと型を追加する

:::

という特徴になります。  

`FrontendEngineer` 型は `Engineer` 型を引き継いでいるから、  
`kakedashiFront` オブジェクトの `FrontendEngineer` 型を `Engineer` 型としても問題ありません。  

これが部分型関係となります。

## 5.2.SuperType にオプショナルなプロパティを追加する

[部分型とは？](#51部分型とは)では、  
`Engineer` 型なのにエンジニアのスキルがなく違和感を感じたかと思います。

ここでは、`Engineer` 型にオプショナルなプロパティの `frontendSkill` を追加してみます。

```ts:部分型サンプルコード
type Engineer = {
    name: string;
    year: number;
    frontendSkill?: Array<string>; // ← 追加
}

type FrontendEngineer = {
    name: string;
    year: number;
    frontendSkill: Array<string>;
}

const kakedashiFront: FrontendEngineer = {
    name: 'daishi',
    year: 1,
    frontendSkill: ['JavaScript']
};

const kakedashiEngineer: Engineer = kakedashiFront;

/**
 * 実行結果
 * { name: 'daishi', year: 1, frontendSkill: [ 'JavaScript' ] }
 */
console.log(kakedashiEngineer);
```

ただ、ここでも、
「エンジニアなのにフロントしか定義しないの？」っていうツッコミ👋 はとりあえず置いておいてください。 

これは
実行してもコンパイルエラーになりません。  

> `name` と `year` 以外が **あってもなくても** 問題ありません。  
 だけど、**ないかも** しれへんプロパティにアクセスするとコンパイルエラーになります。

と記述しましたが、  
ここで **ないかもしれない** プロパティを定義しただけになります。
  
このように、下位の型（SubType） の`FrontendEngineer` 型のプロパティを 上位の型（SuperType） の `Engineer` 型が違う型としてもっていても問題ありません。




## 5.3.SuperType を SubType に代入してみると

先程は、`FrontendEngineer` （SubType）型の値を `Engineer` （SuperType）型に代入しても問題なかったですが、  
逆に `Engineer` （SuperType）型の値を `FrontendEngineer` （SubType）型に代入するとどうなるか見てみます。

```ts
type Engineer = {
    name: string;
    year: number;
    frontendSkill?: Array<string>;
}

type FrontendEngineer = {
    name: string;
    year: number;
    frontendSkill: Array<string>;
}

const kakedashiEngineer: Engineer = {
    name: 'daishi',
    year: 1,
    frontendSkill: ['JavaScript']
};

// SuperType の値を SubType 型に代入
const kakedashiFrontendEngineer: FrontendEngineer = kakedashiEngineer;
```

これをコンパイルしてみると

:::note alert

Type 'Engineer' is not assignable to type 'FrontendEngineer'.  
Property 'frontendSkill' is optional in type 'Engineer' but required in type 'FrontendEngineer'.  

翻訳すると
> 'Engineer' 型は 'FrontendEngineer' 型に割り当てることができません。  
  'frontendSkill' プロパティ は、'Engineer' 型ではオプションですが、'FrontendEngineer' 型では必須です。

:::

つまり、
`frontendSkill` プロパティは、

SuperType の値を SubType には代入できないことが解るかと思います。




## 5.4.Engineer 型にプロパティを追加

`Engineer` 型に `backendSkil`プロパティ、 `infraSkill` プロパティを追加してみます。

ここまで `Engineer` 型に `frontendSkill` プロパティしかなく違和感を感じた方もいらっしゃるかと思いますが、  
ここで `Engineer` 型に他のスキルのプロパティも追加してみます。  

```ts:
type Engineer = {
    name: string;
    year: number;
    frontendSkill?: Array<string>;
    backendSkill?: Array<string>; // ← 追加
    infraSkill?: Array<string>;   // ← 追加
}

type FrontendEngineer = {
    name: string;
    year: number;
    frontendSkill: Array<string>;
}

const kakedashiFront: FrontendEngineer = {
    name: 'daishi',
    year: 1,
    frontendSkill: ['JavaScript']
};

const kakedashiEngineer: Engineer = kakedashiFront;

/**
 * 実行結果
 * { name: 'daishi', year: 1, frontendSkill: [ 'JavaScript' ] }
 */
console.log(kakedashiEngineer);
```

`Engineer` 型の `backendSkill` プロパティ、 `infraSkill` プロパティ共に、`frontendSkill` プロパティ同様にオプショナルなプロパティとなっています。  
つまり、`backendSkill` プロパティ、 `infraSkill` プロパティは、あってもなくてもいい型です。


なので、
SuperType の `Engineer` 型を引き継いだ SubType の`FrontendEngineer` 型に、`backendSkill` プロパティ、 `infraSkill` プロパティが存在しなくてもエラーとなりません。




## 5.5.少し複雑な部分型

少し応用編として、  
今まで見てきた `Engineer` 型と `FrontendEngineer` 型の部分型の関係を使って、  
`EngineerTeam` 型と `FrontendEngineerTeam` 型というチームの型を見てみます。

`EngineerTeam` 型は、`FrontendEngineerTeam` 型の部分型の関係になります。

```ts:複雑な部分型サンプルコード
type Engineer = {
    name: string;
    year: number;
    frontendSkill?: Array<string>;
}

type FrontendEngineer = {
    name: string;
    year: number;
    frontendSkill: Array<string>;
}

type EngineerTeam = {
    teamName: string;
    director: Engineer;
    chief: Engineer;
    employee: Engineer;
}

type FrontendEngineerTeam = {
    teamName: string;
    director: FrontendEngineer;
    chief: FrontendEngineer;
    employee: FrontendEngineer;
}

const bucho: FrontendEngineer = {
    name: 'ぶちょう',
    year: 20,
    frontendSkill: ['TypeScript', 'React', 'Vue'],
};

const katyo: FrontendEngineer = {
    name: 'かちょう',
    year: 10,
    frontendSkill: ['TypeScript', 'React'],
};

const kakedashi: FrontendEngineer = {
    name: 'かけだし',
    year: 1,
    frontendSkill: ['JavaScript'],
};

const frontendTeam: FrontendEngineerTeam = {
    teamName: 'frontendTeam',
    director: bucho,
    chief: katyo,
    employee: kakedashi,
};

const engineerTeam: EngineerTeam = frontendTeam;

/**
 * 実行結果
 * {
 *   teamName: 'frontendTeam',
 *   director: {
 *     name: 'ぶちょう',
 *     year: 20,
 *     frontendSkill: [ 'TypeScript', 'React', 'Vue' ]
 *   },
 *   chief: {
 *     name: 'かちょう',
 *     year: 10,
 *     frontendSkill: [ 'TypeScript', 'React' ]
 *   },
 *   employee: {
 *     name: 'かけだし',
 *     year: 1,
 *     frontendSkill: [ 'JavaScript' ]
 *   }
 * }
 */
console.log(engineerTeam);


```

今まで同様に  
`const engineerTeam: EngineerTeam = frontendTeam;`  
はコンパイルエラーとなりません。  

`FrontendEngineerTeam` 型が `EngineerTeam` 型を引き継ついでいるかを見てみましょう。  

```ts:
type EngineerTeam = {
    teamName: string;
    director: Engineer;
    chief: Engineer;
    employee: Engineer;
}

type FrontendEngineerTeam = {
    teamName: string;
    director: FrontendEngineer;
    chief: FrontendEngineer;
    employee: FrontendEngineer;
}
```

`director`、`chief`、`employee` の各プロパティ以外は同じなので、これらが引き継いでいる関係を示せれば、  
`FrontendEngineerTeam` 型は、`EngineerTeam` 型を引き継いでいることが言えそうです。

`director`、`chief`、`employee` の各プロパティは、
- `EngineerTeam` 型は、それぞれ `Engineer` 型
- `FrontendEngineerTeam` 型は、それぞれ `FrontendEngineer` 型  

です。

`FrontendEngineer` 型は、`Engineer` 型を引き継いでいる（部分型）の関係でした。  

ということは、
`FrontendEngineerTeam` 型の `director`、`chief`、`employee` の各プロパティは、  
`EngineerTeam` 型の `director`、`chief`、`employee` の各プロパティ型を引き継いでいることが言えますね。

したがって、  
`FrontendEngineerTeam` 型は、`EngineerTeam` 型を引き継いでいる（部分型）となります。


# 6.おわりに

部分型の理解に苦しみましたが、継承と同じような特徴なんだなと認識すると部分型掴むことができました。

ご指摘頂いた @kazatsuyu 様ありがとうございました。

部分型の概念を理解できたので、TypeScript の世界がもっと広がればいいなと思います。

併せて他の記事も読んでいただけると嬉しいです🙇‍♂️

https://qiita.com/daishiman/items/5acf803f252b82f970fe

https://qiita.com/daishiman/items/324dcc0eae72eb6b13cf

https://qiita.com/daishiman/items/d382279a2a6cae3ea1aa

https://qiita.com/daishiman/items/42ba60a223cadbd98104


最後まで読んでいただきありがとうございました。



# 7.参考

[書籍：プロを目指す人のためのTypeScript入門　鈴木僚太［著］](https://twitter.com/uhyo_/status/1517158176365641729?s=20&t=5jZBSZVbLrAsgkCUruO7Ig) @uhyo