[Medium] Interface vs Type Alias
1. What are Interface and Type Alias?
Interface と Type Alias とは何ですか?
Interface(インターフェース)
定義:オブジェクトの構造を定義し、オブジェクトが持つべきプロパティとメソッドを記述します。
interface User {
name: string;
age: number;
email?: string; // オプションプロパティ
}
const user: User = {
name: 'John',
age: 30,
};
Type Alias(型エイリアス)
定義:型にエイリアスを作成し、オブジェクトに限らず任意の型に使用できます。
type User = {
name: string;
age: number;
email?: string;
};
const user: User = {
name: 'John',
age: 30,
};
2. Interface vs Type Alias: Key Differences
Interface と Type Alias の主な違い
1. 拡張方法
Interface:extends を使用
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
const dog: Dog = {
name: 'Buddy',
breed: 'Golden Retriever',
};
Type Alias:交差型(Intersection)を使用
type Animal = {
name: string;
};
type Dog = Animal & {
breed: string;
};
const dog: Dog = {
name: 'Buddy',
breed: 'Golden Retriever',
};
2. マージ(Declaration Merging)
Interface:マージをサポート
interface User {
name: string;
}
interface User {
age: number;
}
// 自動的に { name: string; age: number; } にマージされる
const user: User = {
name: 'John',
age: 30,
};
Type Alias:マージをサポートしない
type User = {
name: string;
};
type User = { // ❌ エラー:Duplicate identifier 'User'
age: number;
};
3. 適用範囲
Interface:主にオブジェクト構造に使用
interface User {
name: string;
age: number;
}
Type Alias:任意の型に使用可能
// 基本型
type ID = string | number;
// 関数型
type Greet = (name: string) => string;
// ユニオン型
type Status = 'active' | 'inactive' | 'pending';
// タプル
type Point = [number, number];
// オブジェクト
type User = {
name: string;
age: number;
};
4. 計算プロパティ
Interface:計算プロパティをサポートしない
interface User {
[key: string]: any; // インデックスシグネチャ
}
Type Alias:より複雑な型演算をサポート
type Keys = 'name' | 'age' | 'email';
type User = {
[K in Keys]: string; // マッピング型
};
3. When to Use Interface vs Type Alias?
いつ Interface を使うか?いつ Type Alias を使うか?
Interface を使用する場合
-
オブジェクト構造の定義(最も一般的)
interface User {name: string;age: number;} -
宣言マージが必要な場合
// サードパーティパッケージの型定義を拡張interface Window {myCustomProperty: string;} -
クラスのコントラクトを定義する場合
interface Flyable {fly(): void;}class Bird implements Flyable {fly(): void {console.log('Flying');}}
Type Alias を使用する場合
-
ユニオン型や交差型の定義
type ID = string | number;type Status = 'active' | 'inactive'; -
関数型の定義
type EventHandler = (event: Event) => void; -
タプルの定義
type Point = [number, number]; -
マッピング型や条件型が必要な場合
type Partial<T> = {[P in keyof T]?: T[P];};
4. Common Interview Questions
よくある面接の質問
問題 1:基本的な違い
以下の2つの定義方法の違いを説明してください。
// 方法 1:Interface を使用
interface User {
name: string;
age: number;
}
// 方法 2:Type Alias を使用
type User = {
name: string;
age: number;
};
クリックして回答を表示
共通点:
- どちらもオブジェクト構造の定義に使用可能
- 使用方法は完全に同じ
- どちらも拡張と継承が可能
相違点:
-
宣言マージ:
// Interface はサポートinterface User {name: string;}interface User {age: number;}// 自動的に { name: string; age: number; } にマージ// Type Alias はサポートしないtype User = { name: string; };type User = { age: number; }; // ❌ エラー -
適用範囲:
// Interface は主にオブジェクトに使用interface User { ... }// Type Alias は任意の型に使用可能type ID = string | number;type Handler = (event: Event) => void;type Point = [number, number];
推奨:
- オブジェクト構造の定義時はどちらも使用可能
- 宣言マージが必要な場合は Interface
- 非オブジェクト型の定義には Type Alias
問題 2:拡張方法
以下の2つの拡張方法の違いを説明してください。
// 方法 1:Interface extends
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
// 方法 2:Type intersection
type Animal = {
name: string;
};
type Dog = Animal & {
breed: string;
};
クリックして回答を表示
Interface extends:
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
// 等価
interface Dog {
name: string;
breed: string;
}
Type intersection:
type Animal = {
name: string;
};
type Dog = Animal & {
breed: string;
};
// 等価
type Dog = {
name: string;
breed: string;
};
違い:
- 構文:Interface は
extendsを使用、Type は&を使用 - 結果:両者の結果は同じ
- 可読性:Interface の
extendsの方が直感的 - 柔軟性:Type の
&は複数の型を組み合わせ可能
例:
// Interface:1つしか extends できない
interface Dog extends Animal {
breed: string;
}
// Type:複数を組み合わせ可能
type Dog = Animal & Canine & {
breed: string;
};
問題 3:宣言マージ
以下のコードの動作を説明してください。
interface User {
name: string;
}
interface User {
age: number;
}
const user: User = {
name: 'John',
// age が欠けるとどうなるか?
};
クリックして回答を表示
interface User {
name: string;
}
interface User {
age: number;
}
// 2つの宣言が自動的にマージされる:
// interface User {
// name: string;
// age: number;
// }
const user: User = {
name: 'John',
// ❌ エラー:Property 'age' is missing in type '{ name: string; }' but required in type 'User'
};
正しい書き方:
const user: User = {
name: 'John',
age: 30, // ✅ age を含める必要がある
};
宣言マージの活用シーン:
// サードパーティパッケージの型定義を拡張
interface Window {
myCustomProperty: string;
}
// 使用可能になる
window.myCustomProperty = 'value';
注意:Type Alias は宣言マージをサポートしない
type User = { name: string; };
type User = { age: number; }; // ❌ エラー:Duplicate identifier
問題 4:実装(implements)
Interface と Type Alias のクラス実装における違いを説明してください。
// ケース 1:Interface
interface Flyable {
fly(): void;
}
class Bird implements Flyable {
fly(): void {
console.log('Flying');
}
}
// ケース 2:Type Alias
type Flyable = {
fly(): void;
};
class Bird implements Flyable {
fly(): void {
console.log('Flying');
}
}
クリックして回答を表示
どちらも implements に使用可能:
// Interface
interface Flyable {
fly(): void;
}
class Bird implements Flyable {
fly(): void {
console.log('Flying');
}
}
// Type Alias(オブジェクト型)
type Flyable = {
fly(): void;
};
class Bird implements Flyable {
fly(): void {
console.log('Flying');
}
}
違い:
- Interface:伝統的にクラスのコントラクト定義に多く使用
- Type Alias:使用可能だが、意味的には Interface の方が適切
推奨:
- クラスのコントラクト定義には Interface を優先
- Type Alias で定義済みの場合も実装可能
注意:Type Alias で定義した関数型は実装不可
type Flyable = () => void;
class Bird implements Flyable { // ❌ エラー:オブジェクト型のみ実装可能
// ...
}
5. Best Practices
ベストプラクティス
推奨される方法
// 1. オブジェクト構造の定義には Interface を優先
interface User {
name: string;
age: number;
}
// 2. ユニオン型の定義には Type Alias
type Status = 'active' | 'inactive' | 'pending';
type ID = string | number;
// 3. 関数型の定義には Type Alias
type EventHandler = (event: Event) => void;
// 4. 宣言マージが必要な場合は Interface
interface Window {
customProperty: string;
}
// 5. クラスのコントラクト定義には Interface
interface Flyable {
fly(): void;
}
class Bird implements Flyable {
fly(): void {}
}
避けるべき方法
// 1. Interface と Type Alias を混在させて同じ構造を定義しない
interface User { ... }
type User = { ... }; // ❌ 混乱を招く
// 2. Type Alias でシンプルなオブジェクト構造を定義しない(特別な要件がない限り)
type User = { // ⚠️ Interface の方が適切
name: string;
};
// 3. Interface で非オブジェクト型を定義しない
interface ID extends string {} // ❌ サポートされない
type ID = string | number; // ✅ 正しい
6. Interview Summary
面接のまとめ
クイックリファレンス
Interface(インターフェース):
- 主にオブジェクト構造の定義に使用
- 宣言マージ(Declaration Merging)をサポート
extendsで拡張- クラスのコントラクト定義に適している
Type Alias(型エイリアス):
- 任意の型に使用可能
- 宣言マージをサポートしない
&交差型で拡張- ユニオン型、関数型、タプルの定義に適している
面接の回答例
Q: Interface と Type Alias の違いは何ですか?
"Interface と Type Alias はどちらもオブジェクト構造の定義に使用できますが、いくつかの重要な違いがあります:1) Interface は宣言マージをサポートし、同じ Interface を複数回宣言して自動マージできます。Type Alias はサポートしません。2) Interface は主にオブジェクト構造に使用されます。Type Alias はユニオン型、関数型、タプルなど任意の型に使用できます。3) Interface は extends で拡張、Type Alias は交差型 & で拡張します。4) Interface はクラスのコントラクト定義に適しています。一般的に、オブジェクト構造の定義にはどちらも使えますが、宣言マージが必要な場合は Interface、非オブジェクト型の定義には Type Alias を使う必要があります。"
Q: いつ Interface を使い、いつ Type Alias を使うべきですか?
"Interface を使う場合:オブジェクト構造の定義、宣言マージが必要な場合(サードパーティパッケージの型拡張など)、クラスのコントラクト定義。Type Alias を使う場合:ユニオン型や交差型の定義、関数型の定義、タプルの定義、マッピング型や条件型が必要な場合。簡単に言えば、オブジェクト構造には Interface を優先し、その他の型には Type Alias を使用します。"