TypeScriptのinterfaceとtypeの違いは？どっち使えばいいの？

TypeScriptの型宣言について調べていると`interface`による宣言と`type`による宣言が出てきます。
`interface`ってなんだっけ？とよくなったのでここらでキッチリ倒しておきたいと思い記事に起こします！

:::note
今後`interface`と`type`宣言の違いを理解して、今後どちらを使っていいのか迷わないようになるための記事です！
:::

## TypeScriptの`interface`と`type`それぞれの特徴
サバイバルTypeScriptの参考コードがわかりやすかったのでそちらを抜粋して記事にします！

https://typescriptbook.jp/reference/object-oriented/interface/interface-vs-type-alias

### `interface`と型エイリアスの特徴早見表

特徴が表にまとまってました。
具体的にコードを参考に調べていきます！

|内容|インターフェイス|型エイリアス|
|--|--|--|
|継承|可能|不可ただし交差型で表現は可能|
|継承による上書き|上書きまたはエラー|フィールド毎に交差型が計算される|
|同名のものを宣言|定義がマージされる|エラー|
|Mapped Types|使用不可|使用可能|

### TypeScriptの継承とは？
`interface`で定義すると`interface`で定義された型と型エイリアスで定義された型両方を`extends`を利用することによって継承できます。

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

type Creature = {
  dna: string;
};

interface Dog extends Animal, Creature {
  dogType: string;
}
```

型エイリアスは継承できませんが、交差型（&）を使用して継承と同じようなことができます！


```typescript
type Animal = {
  name: string;
};

type Creature = {
  dna: string;
};

type Dog = Animal &
  Creature & {
    dogType: string;
  };
```

### TypeScriptの継承による上書き
`interface`でプロパティをオーバーライドすると継承元のプロパティが上書きされます。

```typescript
interface Animal {
  name: any;
  price: {
    yen: number;
  };
  legCount: number;
}

// Animalを継承してnameとpriceのyenを上書きします
interface Dog extends Animal {
  name: string; // 上書き
  price: {
    yen: number;
    dollar: number;
  };
}

// 最終的なDogの定義
// interfaceでオーバーライドできない場合はエラーになります
interface Dog {
  name: string;
  price: {
    yen: number;
    dollar: number;
  };
  legCount: number;
}
```

ただ、`number型`をstring型`ではオーバーライドできないなど、元の型に代入できる型であることが条件です。

:::note warn
一度定義した型を別のところでオーバーライドすることは、予期せぬ不具合を招く恐れがあるのでやめた方が良いのではないかと考えております…。
:::

型エイリアスの場合は`interface`のように上書きはできません。

```typescript
type Animal = {
  name: number;
  price: {
    yen: number;
    dollar: number;
  };
};

type Dog = Animal & {
  name: string;
  price: {
    yen: number;
    euro: number;
  };
};

// 最終的なDogの定義
// interfaceのようにエラーは出ませんがnever型になります
type Dog = {
  name: never;
  price: {
    yen: number;
    dollar: number;
    euro: number;
  };
};
```

### TypeScriptで同名のものを宣言
型エイリアスは**同名では複数回定義できずコンパイルエラー**になります。
`interface`だと同名の定義が可能です。

```typescript
interface SameNameInterfaceIsAllowed {
  myField: string;
  sameNameSameTypeIsAllowed: number;
  sameNameDifferentTypeIsNotAllowed: string;
}

interface SameNameInterfaceIsAllowed {
  newField: string;
  sameNameSameTypeIsAllowed: number;
}

interface SameNameInterfaceIsAllowed {
  // 同名のフィールドで型の定義が間違っている場合エラーになります。
  sameNameDifferentTypeIsNotAllowed: number;
}
```

### Mapped Types
「Mapped Types」は型のキーを動的に指定できる仕組みで、型エイリアスでのみ利用できます。

```typescript
type SystemSupportLanguage = 'en' | 'fr' | 'it' | 'es';
type Butterfly = {
  [key in SystemSupportLanguage]: string;
};

// 結果
type Butterfly = {
  en: string;
  fr: string;
  it: string;
  es: string;
}
```

## 【まとめ】`interface`と`type`どっち使えばいいの？
結論から言うと正解はないです…が個人としては**型エイリアスを利用するのがいいのでは？**と考えています。
その理由は
- `interface`はDeclaration mergingされる
- `interface`ではunion型やtuple型は定義できない

### Declaration mergingという機能
サバイバルTypeSTypeScriptで挙げられていた複数定義した場合にくっつけてくれる機能のことです！
外部のライブラリの型を拡張する時には良いですが、グローバルに定義されている型があった場合に**意図せず拡張してしまう**ケースもあると思います…。

### union型やtuple型を使う場合は型エイリアス
使う必要がないなら`interface`を利用するで良いですが、使わないのは少し厳しいのかも…と実装経験が少ないながら感じます。

`interface`で型を定義することのメリットがないというわけではありませんが、
型エイリアスの定義で全てをカバーできるなら型エリアスで統一して定義した方がいいのかな…というのが現時点での結論です。