こんにちは、株式会社ファミトラでエンジニアをしているおおさわです。
前回は TypeScript のプリミティブ型について整理しました。
今回はオブジェクトや構造を表現する 型定義 についてまとめたいと思います。
前回の記事
type と interface
type
と interface
の違い
TypeScript では型を表現する手段として type
と interface
の2種類があります。これらは似ているようで、挙動が微妙に異なります。
(厳密には interface
は型定義、type
は型エイリアスと呼ばれていますが)
代表的な違いは下記のとおりです
1. 定義の書き方
// type
type User = {
id: number;
name: string;
};
// interface
interface User {
id: number;
name: string;
}
どちらもオブジェクトの型を定義できますが、type
の場合は =
が必要です。
2. 拡張性(継承・拡張)
interface は extends
を使って拡張できます。
interface Person {
name: string;
}
interface Employee extends Person {
employeeId: number;
}
一方、type は交差型 &
を使うことで同等のことが可能です。
type Person = { name: string };
type Employee = Person & { employeeId: number };
3. 再定義時の挙動
interface は同じ名前で定義すると自動的にマージされます。
この仕組みは declaration merging(宣言マージ) と呼ばれます。
interface User {
id: number;
}
interface User {
name: string;
}
// => { id: number; name: string } になる
type は同じ名前で再定義するとエラーになります。
4. 柔軟性
type
ではユニオン型・交差型・タプル・条件型などを使ってより柔軟に型を構成できます。
またユーティリティ型と組み合わせることで、既存の型を加工して新しい型を作ることもできます。
type User = {
id: number;
name: string;
email: string;
};
// Pick で一部のプロパティだけ抽出
type UserSummary = Pick<User, "id" | "name">;
// => { id: number; name: string } になる
interface と type の使い分けはどうするべき?
ここまで違いをあ挙げてきましたが、結局のところほとんどの場面で両方とも同じように使えそうです。
強いて言うなら、個人的にはそれぞれ以下のように使い分けるのが良いのかなと思いました
-
interface
- 宣言マージが効くため拡張性が高いので、外部公開用の型(ライブラリの API やコンポーネントの Props など)で有用そう
-
type
- 複雑な型を扱いやすく、アプリケーション内部の型定義や API レスポンスのモデリングに向いていそう
また、ファミトラでの開発では明確にルール化していませんが、おおまかに次のように使い分けています(本来はコーディング規約として明示しておくのが望ましいかもですね・・・)
-
interface
- コンポーネントの Props の型を定義するとき
-
type
- API で取得するオブジェクトの型
- 外部公開を前提としないコンポーネントや関数内部専用のオブジェクトの型
ここまで type
とinterface
について簡単にまとめてみたものの、ネットを見ている感じ使い分けに関して宗教に近い何かを感じました。
TypeScriptを使っているチームでレビュー中に宗教戦争を勃発させたくなさそうなら早めに明示的にルールを定めたほうが良いかもですね