TypeScriptのインターフェースについて

## 事前知識
インターフェースの説明をするにあたり、クラスを理解する必要がある。
下記記事でまとめているので参考までに。

https://qiita.com/toraguitar/private/b9cba23ed8e0a52fe7a4

https://qiita.com/toraguitar/private/57d600d208efd7d75121

## インターフェース
オブジェクトのフィールドやメソッドを定義するための型。
`inferface`を用いてインターフェースを定義できる。

```ts
interface Music {
    name: string;
    debutYear: number;

    show(): void; // 処理は書かず、型定義のみ行う
};
```

## type(カスタム型)との使い分け
interfaceで書く型定義は、typeでも書くことができる。
```ts
type Music = {
    name: string;
    debutYear: number;

    show(): void;
};
```
typeはオブジェクトに限らずunion型など様々な方の定義ができる。
オブジェクトの型定義をしたいということを明確にしたい場合、interfaceを用いる。
実際のところ区別は曖昧である。

## インターフェースの使い方
### implements

クラスの定義時、`implements`でインターフェースを記述する。

```ts
interface Music {
    name: string;
    debutYear: number;

    show(): void;
};

// Rockクラス宣言時、implementsでインターフェースに従う
class Rock implements Music {
    name: string;
    debutYear: number;
  
    constructor(n: string, y: number) {
        this.name = n;
        this.debutYear = y;
    }

    // 引数の型・有無など、定義に合っていれば内容はなんでも問題ない
    show() {
        console.log(`名前: ${this.name} デビュー年: ${this.debutYear}年`);
    }
};
```

インターフェースはオブジェクトの構造を定義するのみであり、
定義されたプロパティやメソッドの存在を担保するものである。
それ以外の制約は課さない。

インターフェースに定義されているプロパティが存在していない、型が異なる場合はエラーになる。
一方で、定義されていないプロパティ・メソッドの存在についてはエラーを出さない。

```ts
interface Music {
    name: string;
    debutYear: number;

    show(): void;
};

class Rock implements Music {
    name: string;
    debutYear: string; // -> NG: インターフェースと型が異なる
    age = 30; // -> OK: 定義されていないプロパティの宣言はエラーにならない
  
    constructor(n: string, y: number) {
        this.name = n;
        this.debutYear = y;
    }

    // 引数の型・有無など、定義に合っていれば内容はなんでも問題ない
    show() {
        console.log(`名前: ${this.name} デビュー年: ${this.debutYear}年`);
    }
};
```

あまり頻度は高くないが、関数の構造を定義することも可能。
```ts
// 引数と戻り値の型を定義するのみ
interface AddFn {
    (a: number, b: number): number;
}

// AddFn型として使用可能
const add: AddFn = (n1: number, n2: number) => {
    return n1 + n2;
}

```
## 読み取り専用のインターフェース
インターフェースには修飾子`readonly`を使用できる。`public`, `private`は使用できない。

:::note info
**readonlyとは**
プロパティを読み取り専用にしたい場合につける修飾子。
newでの宣言時に設定したプロパティの値を上書きできなくなる。
:::


```ts
interface Music {
    readonly name: string; // 読み取り専用の指定をする
    debutYear: number;
}

class Pop implements Music {
    name: string;
    debutYear: number;

    constructor(n: string, d: number) {
        this.name = n;
        this.debutYear = d;
    }
};

const music1: Music = new Pop('YOASOBI', 2019);
music1.name = 'OASOBI' // -> Music型でreadonlyを指定しているため、上書きできない

const music2 = new Pop('エゾシカ', 2019); // 型推論でPop型になる
music2.name = 'ヨルシカ'; // -> Pop型でreadonlyを指定していないため、上書きできる
```


## 継承
インターフェースは継承することができる。
クラスの継承と異なり、一つのインターフェースに対して複数継承することができる。

```ts
interface Named {
    name: string;
}
// MusicにNamedを継承する
interface Music extends Named {
    debutYear: number;

    show(): void;
};
```

オブジェクトの定義時に継承することもできる。
```ts
interface Named {
    name: string;
}

interface Music {
    debutYear: number;

    show(): void;
};

// カンマ区切りで複数継承することができる
class Rock implements Music, Named {
    ...
};
```

## オプショナルなプロパティの設定
インターフェースは任意で省略できるプロパティを設定できる。

```ts
interface Music {
    name?: string; // ?を付与することで、nameを省略可能なプロパティにする
}

class Pop implements Music {
    name?: string; // 

    constructor(n?: string) {
        this.name = n;
    }
};

const music1: Music = new Pop(); // nameの値を設定しなくてもエラーにならない
const music2: Music = new Pop('YAMADA'); // nameの値を設定することもできる

console.log(music1.name); // -> undefined
console.log(music2.name); // -> 'YAMADA'
```