TypeScriptを実用している中で、typeとinterfaceの使い道の違いがわからなかったので、今回まとめることにしました。
定義の方法と用途
interface
主にオブジェクトの形状(プロパティやメソッド)を定義するために使われます。
インターフェースは拡張(継承)が可能で、他のインターフェースやクラスを継承することができます。
interface Person {
name: string;
age: number;
greet(): void;
}
class User implements Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, my name is ${this.name}`);
}
}
type(型エイリアス)
型エイリアスは、型に対して新しい名前を付けるために使われます。
プリミティブ型、ユニオン型、タプル、関数型など、さまざまな型に対してエイリアスを作成でき、オブジェクト型に限らず、複雑な型表現を簡単に再利用できるようにします。
type ID = string | number;
type User = {
name: string;
age: number;
}
let userId: ID;
userId = "abc123"; // OK
userId = 12345; // OK
拡張性とマージ
interface
インターフェースは他のインターフェースやクラスを継承することができ、同名のインターフェースが複数定義された場合、自動的にマージされ、プロパティが結合されます。
下記の例では、Animal
インターフェースが2回定義されていますが、マージされて1つのインターフェースになります。
interface Animal {
name: string;
}
interface Animal {
age: number;
}
interface Dog extends Animal {
breed: string;
}
let myDog: Dog = {
name: "Buddy",
age: 5,
breed: "Golden Retriever"
}
type(型エイリアス)
型エイリアスも拡張できますが、&
(インターセクション)や |
(ユニオン)を使って行います。
型エイリアスはインターフェースのようにマージされることはありません。
type Animal = {
name: string;
}
/*
* 同じ名前のtypeは再定義でエラーになる
* マージされない
*/
type Animal = {
age: number;
}
type Dog = {
breed: string;
}
let myDog: Dog = {
name: "Buddy",
breed: "Golden Retriever"
}
ユニオン型やタプルの定義
interface
インターフェースは、オブジェクトの形状を定義することが主な用途となるため、ユニオンやタプルを直接表現することはできません。
type(型エイリアス)
型エイリアスのユニオン、タプルの定義
type Status = "active" | "inactive" | "pending";
type Point = [number, number];
まとめ
ここまでinterfaceと型エイリアスの違いについてまとめましたが、実際のところどっちを使った方がいいかは、明確な正解はないようでした。
個人的には、オブジェクトの形状を定義し、オブジェクト指向における継承を利用したい場合などは、interface。
ユニオン型やタプルなど、より複雑な型定義を扱いたい場合は、type(型エイリアス)を使う方がいいのではないかと思っています。