TypeScriptを学ぶ上で避けて通れないのが、Interface(インターフェース)です。
Interfaceは、データやコンポーネントの「仕様」を明確に定義し、コードの信頼性と保守性を劇的に向上させます。
この記事では、TypeScriptの Interface とは何かという基本から、オブジェクトの構造を定義する方法、さらにはextendsによる継承・拡張、クラスで利用するimplements、そしてプロパティを任意(オプショナル)にする方法まで、実例を交えて解説しています。
Interfaceをマスターし、より堅牢なTypeScriptコードを書きましょう!
interface(インターフェース)とは何か?
interface(インターフェース)は、コード内でデータ構造の「設計図」を定義するために使われる強力な機能です。
主に、オブジェクトのプロパティ(属性)やメソッド(関数)の仕様を記述します。
interfaceのメリット
interfaceを定義することで、特定のオブジェクトやクラスがその「形」に準拠しているかをコンパイル時にチェックできます。
これにより、実行時エラー(ランタイムエラー)を防ぐのに役立ちます。
特に、大規模なプロジェクトや複数人での開発において、データやクラスの構造に関する明確な契約(コントラクト)を提供し、コードベース全体で一貫性を保つのに役立ちます。
コントラクト(Contract)とは、データやコンポーネントが満たすべき「仕様」や「取り決め」を指す言葉です。
TypeScriptによる型の指定はコントラクトにあたります。
interfaceの基本形
interfaceは、interfaceキーワードを使用して、オブジェクトのプロパティの型を指定します。
interface インターフェース名 {
プロパティ名1: 型1;
プロパティ名2: 型2;
メソッド名1:型3;
...
}こうすることで、対象のオブジェクトの「プロパティ名1」の型は「型1」、「プロパティ名2」の型は「型2」、「メソッド名1」の戻り値の型は「型3」のみを受け付けるようになります。
呼び出すときは、オブジェクト名に対し「: インターフェース名」をつけます。
const オブジェクト名: インターフェース名{
プロパティ名1: 値1;
プロパティ名2: 値2;
メソッド名1:型1;
...
}このとき、このオブジェクトの中のプロパティの値の型が、インターフェースで定義した内容とお一致していないとエラーを表示します。
オブジェクトは指定したインターフェースのすべての必須プロパティとメソッドを持っている必要があります。
interfaceの実例
例えば以下のように使います。
▼インターフェースの定義
interface Person {
name: string;
age: number;
greet(): void;
}Personというインターフェースを定義しています。
このインターフェースをuserオブジェクトに対して使うときは以下のようにします。
const user: Person = {
name: "Alice",
age: 30,
greet() {
console.log(`Hello, my name is ${this.name}`);
}
};void型は、「何も返さない(戻り値がない)関数」を表す型です。
関数が正常に実行を完了するものの、呼び出し元に対して意味のある値(データ)を返さないことを示すために使われます。
必須ではないプロパティ(オプショナル)
オブジェクトに対してinterfaceを使う場合、interfaceで定義したプロパティを必ず記述しなければいけません。
ですが時には、なくてもいい(必須ではない)プロパティを設定したい場合もあります。
そんな時はプロパティ名のすぐ後ろに「?」を使います。
interface インターフェース名{
プロパティ名?: 値;
...
}例えば、nameとageは必須だけど、emailは任意という場合は以下のようにinterfaceを定義します。
interface Person {
name: string;
age: number;
email?: string;
}このinterfaceを使用するときに、?をつけたemailを省略することができます。
const user: Person = {
name: "Alice",
age: 30
};関数の型の定義
interfaceを使って関数の引数と戻り値の型を定義することもできます。
interface インターフェース名{
(パラメータ1: 型1, パラメータ2: 型2): 戻り値の型
}呼び出すときは、関数名の後ろに「: インターフェース名」をつけます。
const 関数名: インターフェース名 = (パラメータ1: 型1, パラメータ2: 型2) =>{
}
実例
例えば、sourceとsubStringというパラメータはstring型、戻り値はboolean型とする場合は以下のようにします。
このinterfaceを関数に適用する場合は、関数名の後ろに「: インターフェース名」をつけます。
interface SearchFunc {
(source: string, subString: string): boolean;
}const mySearch: SearchFunc = (src, sub) => {
return src.search(sub) > -1;
};extendsによる拡張(継承)
extendsキーワードを使うことで、他のInterfaceを継承(拡張)することができます。
これにより、既存の定義を再利用しつつ新しいプロパティを追加できます。
多重継承(複数のInterfaceの拡張)も可能です。
interface インターフェース名2 extends インターフェース名1{}実例
例えば、Animalというインターフェースにnameプロパティを設定します。
新たにDogインターフェースを作成したときに、extendsを使うことで、ベースとなるAnimalの設定に加えて、新たに犬種を表すbreedプロパティを追加する場合は以下のようにします。
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}Dogインターフェースは、nameとbreedプロパティを持つことが必須となります。
呼び出すときは以下のようになります。
const myDog: Dog = { name: "Shiro", breed: "Shiba" };後から追加(宣言のマージ)
同じスコープ内で同名のInterfaceを複数定義すると、それらは自動的にマージ(統合)され、すべての定義を含む単一のInterfaceとして扱われます。
つまり、既に宣言してあるinterfaceに後から定義を追加することができます。
使い方は簡単で、最初に定義したときと同じように定義するだけです。
interface インターフェース名1 {
プロパティ名1: 型1;
プロパティ名2: 型2;
}
interface インターフェース名1 {
プロパティ名3: 型3;
}↑↓ 同じ
interface インターフェース名1 {
プロパティ名1: 型1;
プロパティ名2: 型2;
プロパティ名3: 型3;
}実例
例えば、heightとwidthを持つBoxインターフェースを定義した後に、再度Boxインターフェースを定義して、depthを追加することができます。
interface Box {
height: number;
width: number;
}
interface Box {
depth: number; // 既存のBoxに追加
}
const box: Box = { height: 5, width: 6, depth: 10 };クラスへの実装(implements)
Interfaceは、クラスが持つべきプロパティやメソッドの「型」を定義する「契約」としても利用できます。
クラス宣言でimplementsキーワードを使ってInterfaceを実装すると、そのクラスはInterfaceで定義されたすべてのメンバーを実装する義務が生じます。
interface インターフェース名 {
プロパティ名1: 型1;
プロパティ名2: 型2;
}
class クラス名 implements インターフェース名{
}実例
例えば、currrentTimeとsetTimeを持つインターフェースをクラスに適用するには以下のようにします。
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { }
}typeエイリアスとの違い
TypeScriptにはInterfaceとよく似た機能としてtypeエイリアス(型エイリアス)があります。
interfaceとtypeエイリアスの主な違いは以下になります。
| 特徴 | Interface | typeエイリアス |
| 定義できるもの | 限定的 主にオブジェクトの構造、クラス、関数の型 | 広範囲 オブジェクト構造、プリミティブ型、ユニオン型、タプル、複雑な型など |
| 拡張性 | extendsキーワードで拡張可能。多重継承も可。 | 交差型(&)を使って拡張に似た合成が可能。 |
| 宣言のマージ | 可能 (同名定義で自動的にマージされる) | 不可能 (※同名定義はエラーになる) |
| クラスの実装 | クラスにimplementsできる | クラスにimplementsできる |


