見出し画像

タイプか、インターフェースか、それが問題だ

TypeScript において、`type` と `interface` は型を定義するために使用され、さまざまな場合においては相互に使用することができます。しかし、いくつかの重要な違いとそれぞれの適用シナリオがあります。`type` と `interface` を深く理解する前に、まずそれらの基本的な使い方を見てみましょう。

1. 基本的な使い方:`type` と `interface` でオブジェクト型を定義

// interface を使用
interface User {
  name: string;
  age: number;
}

// type を使用
type UserType = {
  name: string;
  age: number;
};

const user1: User = { name: 'Alice', age: 25 };
const user2: UserType = { name: 'Bob', age: 30 };

この例では、`interface` と `type` はどちらもオブジェクト型を定義するために使用でき、使用方法にほとんど違いはありません。

2. 拡張(Extend)

2.1 `interface` の拡張

`interface` は継承を使って拡張できます:

interface User {
  name: string;
  age: number;
}

interface Admin extends User {
  role: string;
}

const admin: Admin = {
  name: 'Alice',
  age: 30,
  role: 'Administrator',
};

2.2 `type` の拡張

`type` は交差型(Intersection Types)を使って拡張できます:

type UserType = {
  name: string;
  age: number;
};

type AdminType = UserType & {
  role: string;
};

const adminType: AdminType = {
  name: 'Bob',
  age: 35,
  role: 'Admin',
};

違い:

  • `interface` は `extends` キーワードを使って継承で拡張します。

  • `type` は `&` 交差型を使って拡張します。

3. 宣言のマージ(Declaration Merging)

3.1 `interface` の宣言マージ

`interface` は再宣言を許可し、TypeScript は自動的にそれらのメンバーをマージします。

interface User {
  name: string;
}

interface User {
  age: number;
}

const user: User = { name: 'Alice', age: 25 }; // マージ後の User インターフェースは name と age の両方のプロパティを持ちます

3.2 `type` はマージできない

`type` は宣言マージをサポートしていません。同じ名前の型を再定義するとエラーになります。

type UserType = {
  name: string;
};

// 同名の型を再定義するとエラー
type UserType = {
  age: number; // エラー: 重複した識別子 'UserType'。
};

違い:

  • `interface` は宣言マージをサポートしており、同名のインターフェースがあれば自動的にプロパティがマージされます。

  • `type` は再宣言をサポートしていません。

4. ユニオン型と交差型

4.1 `type` はユニオン型と交差型をサポート

`type` はユニオン型と交差型の定義をサポートし、`interface` はサポートしません。

// ユニオン型
type Status = 'success' | 'error' | 'loading';

// 交差型
type Name = { name: string };
type Age = { age: number };
type Person = Name & Age;

const person: Person = { name: 'Alice', age: 25 };

4.2 `interface` はユニオン型をサポートしない

`interface` はユニオン型を直接定義できません。

// サポートしない
interface Status = "success" | "error" | "loading"; // エラー

違い:

  • `type` はユニオン型と交差型をサポートしているため、複数の型を組み合わせる際により柔軟です。

  • `interface` はユニオン型を定義できませんが、オブジェクト型の拡張には使用できます。

5. 型エイリアス

`type` はオブジェクト以外の型エイリアスを定義するためにも使用できます。例えば、基本型、タプル、ユニオン型などです。

// 基本型エイリアスの定義
type ID = number | string;

// タプル型の定義
type Point = [number, number];

const id: ID = 123;
const point: Point = [10, 20];

一方、`interface` はオブジェクト型の定義にのみ使用できます。

6. 実装(Implements)と継承(Extends)

6.1 `interface` はクラスの実装をサポート

`interface` はクラスの構造を制約するために使用できます。

interface User {
  name: string;
  age: number;
}

class Person implements User {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

6.2 `type` でもクラスの制約が可能

通常、`interface` がクラスの制約に使用されますが、`type` でも似たような効果を得ることができます。

type UserType = {
  name: string;
  age: number;
};

class PersonType implements UserType {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

違い:

  • `interface` はクラスの設計パターンでより一般的に使用されます。なぜなら、宣言マージを許可し、型の拡張能力が強いためです。

  • `type` もクラスの制約に使用できますが、主にユニオン型やその他の複雑な型の組み合わせで使用されます。

7. 相互運用性

7.1 `interface` と `type` は混合して使用可能

`interface` と `type` は実際の開発において互いに組み合わせて使用できます。例えば、`interface` が `type` を拡張することも、その逆も可能です。

type Name = { name: string };

interface User extends Name {
  age: number;
}

const user: User = { name: 'Alice', age: 25 };

7.2 `type` も `interface` を拡張可能

interface User {
  name: string;
  age: number;
}

type Admin = User & {
  role: string;
};

const admin: Admin = { name: 'Bob', age: 35, role: 'Admin' };

違い:

  • `interface` は `type` を拡張でき、`type` も `interface` を拡張できるため、相互に組み合わせて使用できます。

8. 複雑な型の表現能力

8.1 `type` の表現能力がより強力

`type` はユニオン型、交差型、基本型エイリアスなど、複雑な型の組み合わせをサポートしているため、複雑な型を表現する際により柔軟です。

type ComplexType = string | number | { name: string };

const value1: ComplexType = 'Hello'; // 正しい
const value2: ComplexType = 123; // 正しい
const value3: ComplexType = { name: 'Alice' }; // 正しい

8.2 `interface` は主にオブジェクト構造に使用

`interface` は主にオブジェクトの構造を記述するために使用され、ユニオン型や複雑な組み合わせ型のシナリオでは `type` ほど柔軟ではありません。

// interface は直接ユニオン型を定義できません
// interface ComplexType = string | number | { name: string }; // エラー

9. 使用シーンのまとめ

`interface` の適用シーン:

  • オブジェクト型の定義、特にオブジェクト指向設計パターンにおいて。

  • クラスの制約、インターフェースの拡張および実装(`implements`)。

  • 宣言マージが必要なシナリオ(例えば、サードパーティライブラリの型定義)。

`type` の適用シーン:

  • ユニオン型、交差型などの複雑な型定義。

  • 基本型やタプル型のエイリアス。

  • 複数の型を組み合わせる必要があるシナリオでの柔軟な対応。

まとめ

`interface` と `type`

  • オブジェクト型の定義:✅

  • 基本型エイリアスの定義:❌

  • 宣言マージ:✅

  • 拡張方法:`extends` 継承

  • ユニオン型のサポート:❌

  • 交差型のサポート:❌

  • クラスの実装に使用:✅

  • 表現能力:オブジェクト構造の定義に適する

  • よく使用されるシーン:オブジェクト指向設計、クラスとインターフェースの組み合わせ使用

`type`

  • オブジェクト型の定義:✅

  • 基本型エイリアスの定義:✅

  • 宣言マージ:❌

  • 拡張方法:`&` 交差型

  • ユニオン型のサポート:✅

  • 交差型のサポート:✅

  • クラスの実装に使用:✅(ただしあまり一般的ではない)

  • 表現能力:複雑な型の組み合わせに適する

  • よく使用されるシーン:ユニオン型、複雑な型定義、型エイリアスなど

全体的に見ると、interface はオブジェクト指向プログラミングに適しており、特にクラスの設計や継承において有用です。一方、type はより柔軟で、複雑な型の組み合わせや型エイリアスのシナリオに適しています。ほとんどのプロジェクトでは、interface と type は補完的に使用することができます。


私たちはLeapcell、Node.jsプロジェクトのホスティングの最適解です。

Leapcellは、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです:

複数言語サポート

  • Node.js、Python、Go、Rustで開発できます。

無制限のプロジェクトデプロイ

  • 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。

比類のないコスト効率

  • 使用量に応じた支払い、アイドル時間は課金されません。

  • 例: $25で6.94Mリクエスト、平均応答時間60ms。

洗練された開発者体験

  • 直感的なUIで簡単に設定できます。

  • 完全自動化されたCI/CDパイプラインとGitOps統合。

  • 実行可能なインサイトのためのリアルタイムのメトリクスとログ。

簡単なスケーラビリティと高パフォーマンス

  • 高い同時実行性を容易に処理するためのオートスケーリング。

  • ゼロ運用オーバーヘッド — 構築に集中できます。

ドキュメントで詳細を確認!

Xでフォローする:@LeapcellHQ


ブログでこの記事を読む

いいなと思ったら応援しよう!