TypeScript の Interface と Type Alias の違い

TypeScript の Interface と Type Alias は、TypeScript のバージョンが上がるにつれ、どんどん同じような機能を有するようになってきています。

それならどっちかしか必要ないんじゃないか？
Interface で書くか Type Alias で書くかどちらでもよいのなら、迷ってしまいよくないのでは？

そんなことを考えてしまったので、ちゃんと調べてみました。

ちなみに、type は Type Alias または型エイリアスと呼ばれていることが多かったので、本記事でも Type Alias で通しています。

## 三行まとめ
 
- Interface はオープン
- Type Alias はクローズド
- どちらを使えばいいかはケースバイケース

## Interface

> Interface は、オブジェクトや関数、クラスの **仕様を定めるため** のもの

### 用途
- クラスやオブジェクトの規格を定義する
- オブジェクト、クラス、関数の抽象型を定義するのに適している

### 性質
- 継承
    - extends した際に同名のプロパティ（要素）を宣言した場合マージ（上書き）される
    - type も継承できる
- オブジェクトと関数の型宣言のみしか使えない
- 宣言時に `=` が不要
- class への implements が可能
- [MappedTypes](https://qiita.com/SotaSuzuki/items/efe32e07e52dce329653#mappedtype) が使えない
- [index シグネチャ](https://qiita.com/SotaSuzuki/items/efe32e07e52dce329653#index-%E3%82%B7%E3%82%B0%E3%83%8D%E3%83%81%E3%83%A3) が使える
- [宣言されているプロパティ以外のプロパティが存在することが**考慮される**](https://qiita.com/SotaSuzuki/items/efe32e07e52dce329653#%E5%AE%A3%E8%A8%80%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E4%BB%A5%E5%A4%96%E3%81%AE%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E3%81%8C%E5%AD%98%E5%9C%A8%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%AE%E8%80%83%E6%85%AE%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)
- [拡張に対してオープン](https://qiita.com/SotaSuzuki/items/efe32e07e52dce329653#%E6%8B%A1%E5%BC%B5%E3%81%AB%E5%AF%BE%E3%81%97%E3%81%A6%E3%82%AA%E3%83%BC%E3%83%97%E3%83%B3)
    - 同じ Interface を再宣言すると Interface 同士がマージされる

## Type Alias

> Type Alias は、複数の場所で再利用しようと思っている型に対して **名前をつけるため**のもの

### 用途
- 型や型の組み合わせに別名を付ける
- 再利用したい複雑な型（複合的な型など）に名前を付けて再利用可能にする
- 複雑な型の宣言に適している
  - 理由: 交差型, ユニオンタイプ、MappedType が使えるため
  - ユーティリティタイプなども Type Alias を使って書く

### 性質
- 継承
    - [交差型](https://qiita.com/SotaSuzuki/items/efe32e07e52dce329653#%E4%BA%A4%E5%B7%AE%E5%9E%8Bintersection-type)（&）を使えば同じようなことは可能
        - ただし上書きはできない（後述）
    - 交差型で Interface を `&` することも可能
    - `&` した際に同名のプロパティ（要素）を宣言した場合重複する（後述）
- オブジェクトと関数以外の型宣言でも使用可能
    - プリミティブ、配列、タプルなど
- 宣言時に `=` が必要
- class への implements が可能
- MappedTypes が使える
- index シグネチャが使える
- 宣言されているプロパティ以外のプロパティが存在することが **考慮されない**
- 拡張に対してクローズド
    - 同じ type を宣言するとエラー

## 結局どっち使えばええのん？

ケースバイケースです。
強いて言うなら、コードベース（プロジェクト）全体を通した仕様としての型には Interface を、そうではない型には Type Alias を使うのがいいのかなと思いました。

## 参考コード

### index シグネチャ

```tsx
interface Options { [key: string]: any; }

function getUser (options?: Options = {}) {
  // ...
}
const options = { params: { id: 21 } }
let user = getUser(options); // OK
```

### MappedType

普通にやってたらあまり使わないけど、便利タイプを作る時とかわりと使うかも。
`keyof`, `typeof` などと合わせて使うことが多いかも。

```tsx
type Fruits = 'Apple' | 'Orange' | 'WaterMelon';

// {
//   'Apple': number;
//   'Orange': number;
//   'WaterMelon': number;
// }
type PriceOfFruits = { [key in Fruits]: number; }

// NG
interface PriceOfFruits { [key in Fruits]: number; }
```

### 交差型（Intersection Type）

通称かつオペレータ。上書きしようとした場合、両方を満たす必要が出てくる。制約を追加するイメージ（まさに `&`）。

```tsx
type Point = { x: number, y: number }
type LabelledPoint = Point & { label: string } // GOOD

type Point2 = Point & { y: number | string } // NOT GOOD
const p2: Point2 = { x: 20, y: '20px' } // Error! y は number しか許容しない
```

### 拡張に対してオープン

```tsx
interface User {
  id: number;
  name: string;
}

interface User {
  age: number;
}

// User {
//   id: number;
//   name: string;
//   age: number;
// }
```

### 宣言されているプロパティ以外のプロパティが存在することの考慮について

日本語がわかりづらいので具体的なコードで示す。

```tsx
declare function getX(obj: { [key: string]: number }): number;

type Point = { x: number, y: number }
interface IPoint { x: number, y: number }

const pointA = { x: 1, y: 9, z: 0 }
getX(pointA) // OK

const pointB: Point = { x: 40, y: 120 }
getX(pointB) // OK

const pointC: IPoint = { x: 23, y: 46 }
getX(pointC) // Error!
```

## 参考にした記事

- [インターフェース - TypeScript Deep Dive 日本語版](https://typescript-jp.gitbook.io/deep-dive/type-system/interfaces)

- [TypeScriptのInterfaceとTypeの比較 - Qiita](https://qiita.com/tkrkt/items/d01b96363e58a7df830e)

- [Types vs. interfaces in TypeScript - LogRocket Blog](https://blog.logrocket.com/types-vs-interfaces-in-typescript/)

- [assignabiltity between interfaces and types · Issue #14736 · microsoft/TypeScript](https://github.com/Microsoft/TypeScript/issues/14736)

- [Implicit index signatures by ahejlsberg · Pull Request #7029 · microsoft/TypeScript](https://github.com/microsoft/TypeScript/pull/7029)

- [【翻訳】TypeScriptにおけるInterfaceとType aliasの違い](https://freelance-jak.com/technology/typescript/1988/)

- [TypeScript: Interfaces vs Types](https://stackoverflow.com/questions/37233735/typescript-interfaces-vs-types)