【TypeScript】type VS interface

## TypeScriptにおける型の定義
TypeScriptで型を定義するとき、typeとinterfaceどちらを使えばいいんだ？と思うことはありませんか？
両方とも型を定義する方法ですが、用途の違いがあるのでこの記事ではそれぞれの特徴と使い所を解説します。

## typeとは
type文は**型に名前をつける文**です。
`type 型名 = 型;`のように使用します。
例えば、下記のように宣言することで、`FooBarObj型`を定義することができます。
```typescript
type FooBarObj = {
  foo: number;
  bar: string;
};

// objの型としてFooBarObj型を指定
const obj: FooBarObj = {
  foo: 123,
  bar: "Hello, world!",
};
```

type文はすでにある型に別名をつけるということができ、`型エイリアス`と呼ばれます。
例えば、type文はオブジェクト型ではなくプリミティブ型に対しても利用でき、
このことからも、「型に別名をつける」という動きが理解しやすいかと思います。

```typescript
type UserId = string;

// id: stringと同義
const id: UserId = "123"
```

## interfaceとは
interface宣言は**型名を新規作成する方法**です。
interface宣言ではtype文とは違い、オブジェクト型のみを扱います。
先ほどtype文で書いた`FooBarObj型`をinterfaceで宣言すると、下記のように書くことができます。
```typescript
interface FooBarObj {
  foo: number;
  bar: string;
}

// objの型としてFooBarObj型を指定
const obj: FooBarObj = {
  foo: 123,
  bar: "Hello, world!",
};
```


## typeとinterfaceの違い
オブジェクト型を宣言する場合の方法としては大きな違いはないように見えますが、
両者にはいくつかの違いが存在します。
その1つが継承です。

interfaceは継承が可能ですが、type文は継承ができません。
一方で、type文でも交差型(&)を使用することで継承と似たことを実現することはできます。

```typescript
interface Animal {
  name: string;
}

type Creature = {
  dna: string;
};

// Dogはname, dna, dogTypeのプロパティを持つ
interface Dog extends Animal, Creature {
  dogType: string;
}
```

また、type文では同名のものを複数定義できませんが、interfaceは同名のinterfaceを定義でき、
同名の定義をすべて合成したinterfaceになります。
ただし、同名のプロパティの型定義が異なる場合はコンパイルエラーになります。

```typescript
// 名称が重複しているためコンパイルエラーが発生
type SameNameTypeWillError = {
  message: string;
};
type SameNameTypeWillError = {
  detail: string;
};
```

```typescript
interface SameNameInterfaceIsAllowed {
  myField: string;
  sameNameSameTypeIsAllowed: number;
  sameNameDifferentTypeIsNotAllowed: string;
}
interface SameNameInterfaceIsAllowed {
  newField: string;
  sameNameSameTypeIsAllowed: number;
}
interface SameNameInterfaceIsAllowed {
// すでに定義済みのプロパティと同名で型定義が異なるためコンパイルエラーが発生
  sameNameDifferentTypeIsNotAllowed: number;
}
```

他にも`Mapped Types`と呼ばれる型の再利用方法はtype文のみでしか使用できないという違いがあります。
Mapped Typesは下記のように、主にユニオン型と組み合わせて使用することができます。

```typescript
type SystemSupportLanguage = "en" | "fr" | "it" | "es";

type Butterfly = {
  [key in SystemSupportLanguage]: string;
};
```

上記では、Butterflyの型は下記と同義になります。
```typescript
type Butterfly = {
  en: string;
  fr: string;
  it: string;
  es: string;
};
```

そのため、下記のように`SystemSupportLanguage`で定義されているリテラル型以外のプロパティを追加するとコンパイルエラーが発生します。

```typescript
const butterflies: Butterfly = {
  en: "Butterfly",
  fr: "Papillon",
  it: "Farfalla",
  es: "Mariposa",
  // deはButterflyに存在しないためコンパイルエラーが発生
  de: "Schmetterling",
};
```

Mapped Typesを使用することでキーの制約を与えることができますが、
これはinterfaceでは使用できず、type文のみで使用できます。

## 結局どちらを使うべきなのか
どちらを使うべきなのかという点に関しては明確な正解はないようです。
Googleが公開しているTypeScriptのスタイルガイドでは、
プリミティブな値やユニオン型やタプルの型定義をする場合は型エイリアスを利用し、
オブジェクトの型を定義する場合はインターフェースを使うことを推奨しています。

また、下記のようなそれぞれの主張も存在します。

### type派
- interfaceでできることは大抵typeでもできる
- 知らないうちに拡張されたくないから最初からtypeでよい

### interface派
- オブジェクトの形状を表現するのに適している
- 拡張が可能

私が読んだ[プロを目指す人のためのTypeScript入門](https://gihyo.jp/book/2022/978-4-297-12747-3)では「特定の場合を除いてtype文のみを使っておけば困ることはない」だろうと書かれていましたが、
結局はチームやプロジェクト内でルールを決めてそれを遵守するのが良さそうです。

## 参考文献
- [プロを目指す人のためのTypeScript入門](https://gihyo.jp/book/2022/978-4-297-12747-3)
- [サバイバルTypeScript](https://typescriptbook.jp/)
- [Google TypeScript Style Guide](https://google.github.io/styleguide/tsguide.html)