TypeScriptにおけるtypeとinterfaceの違い
TypeScript にはオブジェクトの型を表現する手段として type を使う方法と interface を使う方法があります。
どちらも機能としてとても似たようなところがあるのですがいくつかの違いもあります。
早速一つずつ見ていきましょう。
type
オブジェクトの型は type を使うことで以下のように表現することができます。
type type Human = {
name: string;
age: number;
birthday: Date;
}
Human = {
name: string
name: string;
age: number
age: number;
birthday: Date
birthday: Date;
};
let let man: Human
man: type Human = {
name: string;
age: number;
birthday: Date;
}
Human;
オプショナル
オブジェクトのプロパティに値があってもなくてもどちらでもいいよという場合は以下のように?
をつけます
type type Human = {
name: string;
age: number;
birthday?: Date | undefined;
}
Human = {
name: string
name: string;
age: number
age: number;
birthday?: Date | undefined
birthday?: Date;
};
const const human: Human
human: type Human = {
name: string;
age: number;
birthday?: Date | undefined;
}
Human = {
name: string
name: "daigo",
age: number
age: 30,
// birthdayがなくてもOK
};
&演算子を使った Type 同士の合体
また以下のように型と型同士を&
を使って組み合わせて使うこともできます。
type type TextMessage = {
text: string;
}
TextMessage = {
text: string
text: string;
};
type type ImageMessage = {
imageUrl: string;
alt: string;
}
ImageMessage = {
imageUrl: string
imageUrl: string;
alt: string
alt: string;
};
type type Message = TextMessage & ImageMessage
Message = type TextMessage = {
text: string;
}
TextMessage &
type ImageMessage = {
imageUrl: string;
alt: string;
}
ImageMessage;
この時型の Message は text に加えて、imageUrl や alt のプロパティを持ちます。
Mapped Types
オブジェクトのキー側のとりうるプロパティが無数にある時やキーの値が変則的である場合には Mapped Types が利用できます。
Mapped Types はキー側を[]
で囲って表現します。
以下はキーが全て string 型であるオブジェクト型を定義しています。
type type Obj = {
[key: string]: any;
}
Obj = {
[key: string
key: string]: any;
};
以下のように[]
内で別の型のキーを全て展開することも可能です。
type type Human = {
name: string;
age: number;
birthday: Date;
}
Human = {
name: string
name: string;
age: number
age: number;
birthday: Date
birthday: Date;
};
type type MaybeHuman = {
name?: string | undefined;
age?: number | undefined;
birthday?: Date | undefined;
}
MaybeHuman = {
[function (type parameter) key
key in keyof type Human = {
name: string;
age: number;
birthday: Date;
}
Human]?: type Human = {
name: string;
age: number;
birthday: Date;
}
Human[function (type parameter) key
key];
};
結果、MaybeHuman は以下の型と同義になります
type type MaybeHuman = {
name?: string | undefined;
age?: number | undefined;
birthday?: Date | undefined;
}
MaybeHuman = {
name?: string | undefined
name?: string;
age?: number | undefined
age?: number;
birthday?: Date | undefined
birthday?: Date;
};
オブジェクト以外の用途としても利用可能
また type はオブジェクトのために用意されたものではありません。string や number, any などありとあらゆる型を定義しておくことができます。
例えば以下のようにとりあえず型が決まってないので仕方なく any にしておき、後で直すという意味合いを込めてTODO
と名付けておくといったユースケースもありそうです。
type type TODO = any
TODO = any;
const const a: any
a: type TODO = any
TODO = {
name: string
name: "daigo",
age: number
age: 30,
};
また、'OK'か'NG'のいずれかの文字列が当てはまる場合の型も type を使って表現できます。
OR の表現に|
を使います。
type type Judge = "OK" | "NG"
Judge = "OK" | "NG";
interface
次は interface を見ていきます。
interface Human {
Human.name: string
name: string;
Human.age: number
age: number;
Human.birthday: Date
birthday: Date;
}
let let man: Human
man: Human;
一件 type とほとんど変わらないように見えますが、interface はどの型も表現できる Type とは違い、Class もしくはオブジェクトのために用意された仕組みになります。
オプショナル
存在してもしなくてもどちらでもいいプロパティは type の時と同じように?で表現できます。
interface Human {
Human.name: string
name: string;
Human.age: number
age: number;
Human.birthday?: Date | undefined
birthday?: Date;
}
extends を利用した interface 同士の組み合わせ
interface では&ではなく extends を使って interface 同士を組み合わせられます。
interface TextMessage {
TextMessage.text: string
text: string;
}
interface ImageMessage {
ImageMessage.imageUrl: string
imageUrl: string;
ImageMessage.alt: string
alt: string;
}
interface Message
extends TextMessage,
ImageMessage {}
interface の上書き
以下のように同じ interface を 2 回定義すると上の interface を下の interface が上書きする形になります。
interface Message {
Message.text: string
text: string;
}
interface Message {
Message.imageUrl: string
imageUrl: string;
Message.alt: string
alt: string;
}
結果として以下の interface と同義になります。
interface Message {
Message.text: string
text: string;
Message.imageUrl: string
imageUrl: string;
Message.alt: string
alt: string;
}
クラスの interface として利用
また interface はオブジェクトだけではなく以下のようにクラスの型を定義する interface として利用することもできます。
クラスが正しく interface の型と一致しているかをチェックするために以下のようにクラスに対して implements を利用します。
interface ICar {
ICar.speed: number
speed: number;
ICar.x: number
x: number;
ICar.run(): void
run(): void;
}
class class Car
Car implements ICar {
Car.speed: number
speed: number;
Car.x: number
x: number;
constructor(speed: number
speed: number) {
this.Car.speed: number
speed = speed: number
speed;
this.Car.x: number
x = 0;
}
Car.run(): void
run() {
this.Car.x: number
x += this.Car.speed: number
speed;
}
}
まとめ
type と interface は似ているようで、type にあって interface にはない機能、またその逆のケースがあることがわかりましたね。
また、例え同じようなユースケースであったとしても、所属しているチームやコントリビュートしているプロジェクトによっても考え方が違うのでそのチームのルールに従って type と interface を使い分けましょう。
著者のおすすめは普段は type を使い、class の implements の時にだけ interface を使うことです。
class 以外の用途では interface でできることのほとんどは type でできますし、さらに interface では表現できない Mapped Types など多くの機能が type に備わっているためです。
Authored by
Godai@steelydylan
Webサービスを作るのが好きなWebエンジニア。子供が産まれたことをきっかけに独立し法人化。サービス開発が大好き。
好きな言語はTypeScript。