TypeScriptでのユニオン型と交差型の使い方を実践的に解説

TypeScriptは、JavaScriptに型システムを導入した言語であり、コードの安全性や可読性を向上させることができます。その中でも、ユニオン型と交差型は、型を柔軟に扱うための強力なツールです。ユニオン型は複数の型のうちどれか1つを選ぶ場合に使い、交差型は複数の型を結合して新しい型を作成します。これらの型を理解し、効果的に使うことは、堅牢で保守性の高いコードを作成するために非常に重要です。本記事では、ユニオン型と交差型の基本から実際の使用例まで、実践的な視点で解説していきます。

目次

ユニオン型とは

ユニオン型は、TypeScriptで複数の異なる型を一つにまとめるための型の一種です。具体的には、ある変数が複数の型のいずれかを持つ可能性がある場合に使用します。ユニオン型を利用することで、柔軟な型指定ができ、関数や変数が複数の型に対応できるようになります。ユニオン型は「|」記号を用いて定義します。

ユニオン型の基本構文

ユニオン型の基本的な構文は以下の通りです。

let value: string | number;
value = "hello";  // OK
value = 42;       // OK

上記の例では、変数 valuestring または number のいずれかの型を持つことができるため、両方の値を割り当てることが可能です。

ユニオン型の利点

ユニオン型を使うと、柔軟性が増し、特に次のようなケースで役立ちます。

  • 異なる入力型に対応する関数を作成したいとき
  • APIのレスポンスが異なる型を返す可能性がある場合

このように、ユニオン型はコードの柔軟性を保ちながら、型の安全性を確保するのに有効です。

交差型とは

交差型(Intersection Type)は、TypeScriptにおいて複数の型を組み合わせて、新しい型を作るための機能です。交差型を使用すると、複数の型のすべてのプロパティやメソッドを持つ新しい型を定義できます。交差型は、複数の型の機能を合成したい場合に非常に便利です。交差型は「&」記号を使って定義します。

交差型の基本構文

交差型の基本的な構文は以下の通りです。

type A = { name: string };
type B = { age: number };

type Person = A & B;

let person: Person = {
  name: "John",
  age: 30
};

この例では、Person 型は AB の両方の型を持っており、nameage の両方のプロパティを持つ必要があります。

交差型の利点

交差型の大きな利点は、異なる型を統合し、それぞれの型が持つすべてのプロパティやメソッドを強制的に含めることです。これにより、複数の機能を持つオブジェクトを扱う場合に役立ちます。

交差型は特に次のような状況で有効です。

  • 複数のインターフェースやクラスを統合し、それらの特徴を持つ新しい型を作成する場合
  • 柔軟性が必要な場面で、すべての要件を満たす型を定義したい場合

このように、交差型は複数の型の機能を一つにまとめる強力な手段です。

ユニオン型と交差型の違い

ユニオン型と交差型は、どちらも複数の型を扱うためのTypeScriptの機能ですが、その目的と使用方法には大きな違いがあります。両者を適切に理解し、状況に応じて使い分けることが重要です。

ユニオン型の特徴

ユニオン型は「ある値が複数の型のどれか一つである」ことを表現します。これは、柔軟に複数の異なる型を扱いたい場合に使用されます。

let value: string | number;
value = "hello";  // OK
value = 42;       // OK

この例では、valuestringnumber のいずれかを持つことができ、どちらかの型に限定されます。

交差型の特徴

交差型は「複数の型を組み合わせて新しい型を作成し、すべての型のプロパティやメソッドを持たせる」ことを意味します。つまり、交差型は指定されたすべての型を満たす必要があります。

type A = { name: string };
type B = { age: number };

type Person = A & B;

let person: Person = {
  name: "John",
  age: 30
};

この場合、Person 型は AB の両方のプロパティを持つため、オブジェクトには nameage の両方が必要になります。

ユニオン型と交差型の使い分け

  • ユニオン型: 複数の型のうちどれか一つを受け入れる場合に使用します。これは、柔軟に異なる型を扱いたい場合に適しています。
  • 交差型: 複数の型のすべてを含む新しい型を作成する場合に使用します。これは、複数の特徴を統合したい場合に有効です。

まとめると、ユニオン型は「選択肢」を提供し、交差型は「統合」を提供するという点で大きな違いがあります。

ユニオン型の応用例

ユニオン型は、実際のプロジェクトで柔軟にデータを扱う際に非常に有用です。特に、関数が複数の異なる型の引数を受け入れる必要がある場合や、異なる型を処理する場面で役立ちます。ここでは、ユニオン型を応用した具体的な例をいくつか紹介します。

APIレスポンスのハンドリング

APIからのレスポンスが異なる型で返ってくることは珍しくありません。例えば、APIが成功時にはオブジェクトを返し、失敗時にはエラーメッセージを返すようなケースです。ユニオン型を使えば、これらの異なるレスポンス型を簡単に処理できます。

type SuccessResponse = {
  data: string;
};

type ErrorResponse = {
  error: string;
};

function handleApiResponse(response: SuccessResponse | ErrorResponse) {
  if ('data' in response) {
    console.log("Success:", response.data);
  } else {
    console.error("Error:", response.error);
  }
}

この関数は、SuccessResponse または ErrorResponse のどちらかの型を受け取り、それに応じた処理を行います。ユニオン型を用いることで、APIレスポンスが成功か失敗かに応じて柔軟に対応することができます。

複数の入力形式に対応する関数

別の例として、関数が異なる形式の入力を受け入れたい場合にも、ユニオン型は便利です。例えば、数値と文字列の両方を受け取れる関数を考えてみましょう。

function formatInput(input: string | number): string {
  if (typeof input === "number") {
    return input.toFixed(2);
  } else {
    return input.trim();
  }
}

console.log(formatInput(123.456));  // 出力: "123.46"
console.log(formatInput("   hello world   "));  // 出力: "hello world"

この関数 formatInput は、number 型と string 型の両方を受け入れ、それぞれに対して適切な処理を行います。ユニオン型のおかげで、入力型に応じた柔軟なロジックを実装できます。

ユニオン型の利点

ユニオン型を使うことで、複数の型に対応した汎用性の高い関数やロジックを作成することができます。これにより、コードの再利用性やメンテナンス性が向上し、エラー処理や入力データのフォーマットなど、さまざまな場面で効果的に使用することが可能です。

交差型の応用例

交差型は、複数の型を統合して新しい型を作成し、それぞれの型の特徴を組み合わせるために利用されます。これにより、異なる型を1つの型として扱い、複数の特性を持つオブジェクトを簡単に扱うことができます。ここでは、交差型を応用した具体的な使用例を紹介します。

オブジェクトの機能統合

例えば、ユーザー情報とアドミン権限情報を持つオブジェクトを作成したい場合、それぞれを別々の型として定義し、交差型を用いて統合できます。

type User = {
  name: string;
  email: string;
};

type Admin = {
  adminLevel: number;
};

type AdminUser = User & Admin;

let adminUser: AdminUser = {
  name: "John",
  email: "john@example.com",
  adminLevel: 1
};

この例では、User 型と Admin 型を AdminUser 型として統合し、1つのオブジェクトで両方の情報を持たせています。交差型を使用することで、異なる性質を持つ型をまとめ、柔軟にオブジェクトの特性を追加することが可能です。

Mixinパターンの実装

交差型は、複数の機能を統合する際にも役立ちます。例えば、DrivableFlyable という2つの能力を持つオブジェクトを作成する場合、交差型を使ってこれらの機能を統合した型を作成できます。

type Drivable = {
  drive(): void;
};

type Flyable = {
  fly(): void;
};

type SuperVehicle = Drivable & Flyable;

let vehicle: SuperVehicle = {
  drive() {
    console.log("Driving...");
  },
  fly() {
    console.log("Flying...");
  }
};

vehicle.drive(); // "Driving..."
vehicle.fly();   // "Flying..."

この例では、SuperVehicle 型は DrivableFlyable の機能を両方持っており、オブジェクトが運転と飛行の両方を実行できるようになっています。交差型を使うことで、複数のクラスやインターフェースからの機能を一つのオブジェクトに集約できます。

交差型の利点と実践的な応用

交差型の大きな利点は、異なる型のプロパティやメソッドを一つの型にまとめることができる点です。これは、複雑なオブジェクトの設計や、複数の特徴を持つインスタンスを管理する際に役立ちます。実際の開発においても、例えばユーザー管理システムで管理者権限と基本的なユーザー情報を統合したり、さまざまな能力を持つキャラクターやエンティティを扱うシステムに適用したりすることができます。

交差型を効果的に使用することで、柔軟かつ強力な型の設計が可能となり、コードの再利用性やメンテナンス性も向上します。

型安全性の強化

ユニオン型と交差型を効果的に活用することで、TypeScriptにおける型安全性を大幅に強化することができます。型安全性とは、コードが実行時に予期せぬエラーやバグを回避するために、型の一貫性を保つことです。これにより、コードがより予測可能で安定した動作を行い、開発者にとっても理解しやすい設計となります。

ユニオン型での型安全性

ユニオン型を使うと、複数の型を扱う場合でも型を厳密にチェックすることができ、想定外の値が渡されるのを防ぎます。ユニオン型を使用した関数では、条件分岐によって型を明示的にチェックすることで、実行時エラーを防ぐことができます。

function processValue(value: string | number) {
  if (typeof value === "string") {
    return value.toUpperCase();
  } else if (typeof value === "number") {
    return value.toFixed(2);
  }
}

console.log(processValue("hello"));  // "HELLO"
console.log(processValue(42));       // "42.00"

この例では、valuestring または number であることをユニオン型で表現しており、それぞれの型に対して異なる処理を行っています。TypeScriptの型システムは、条件分岐によって型の安全性を確保し、無効な操作が行われることを防ぎます。

交差型での型安全性

交差型では、複数の型を結合することで、オブジェクトがすべての型のプロパティやメソッドを持つことを強制します。これにより、欠落したプロパティがないかどうかをチェックし、型の一貫性を保つことができます。

type Name = { name: string };
type Age = { age: number };

type Person = Name & Age;

function printPersonDetails(person: Person) {
  console.log(`Name: ${person.name}, Age: ${person.age}`);
}

let person: Person = { name: "Alice", age: 25 };
printPersonDetails(person);  // "Name: Alice, Age: 25"

この例では、Person 型が NameAge の両方のプロパティを持っていることを保証します。交差型によって、nameage が確実に存在することが型チェックによって確認され、型安全性が強化されています。

型安全性のメリット

ユニオン型や交差型を適切に利用することで、以下のようなメリットがあります。

  • バグの予防: 実行時エラーの発生を防ぎ、コードの信頼性が向上します。
  • コードの読みやすさ: 型注釈があることで、どのようなデータが期待されているかが明確になり、コードの可読性が向上します。
  • メンテナンスの容易さ: 型安全なコードは将来的なメンテナンスが容易で、新しい機能を追加する際にもミスを減らすことができます。

ユニオン型と交差型を使いこなすことで、型安全なTypeScriptコードを実現し、予測可能で安定したシステムを構築することが可能です。

パターンマッチングとユニオン型

ユニオン型は、TypeScriptの柔軟性を活かし、異なる型のデータを効率的に処理できる特徴を持っています。その中でも、パターンマッチングはユニオン型と組み合わせることで、コードをよりシンプルかつ直感的に記述できる強力な手法です。パターンマッチングを活用することで、ユニオン型を使用した型チェックや条件分岐が効率化され、コードの可読性が向上します。

ユニオン型とパターンマッチングの組み合わせ

ユニオン型を使った関数の処理では、typeofinstanceof などを用いて、型ごとに異なる処理を行うことが一般的です。このパターンマッチングを使用することで、コードがシンプルかつ堅牢になります。

type Dog = { bark: () => void };
type Cat = { meow: () => void };

function handlePet(pet: Dog | Cat) {
  if ("bark" in pet) {
    pet.bark();
  } else {
    pet.meow();
  }
}

let myDog: Dog = { bark: () => console.log("Woof!") };
let myCat: Cat = { meow: () => console.log("Meow!") };

handlePet(myDog);  // "Woof!"
handlePet(myCat);  // "Meow!"

この例では、handlePet 関数が Dog または Cat 型のどちらかを受け取る場合、in キーワードを使って型を判別し、対応するメソッドを呼び出しています。Dog 型には bark メソッドがあり、Cat 型には meow メソッドがあるため、それぞれの動物の動作に応じて正しい処理が行われます。

複雑なユニオン型のパターンマッチング

ユニオン型が複雑になっても、パターンマッチングを使用することで、柔軟に対応可能です。例えば、複数のプロパティやメソッドを持つ型を扱う場合でも、TypeScriptは型安全性を維持しつつ正確な処理を実行します。

type Square = { kind: "square"; size: number };
type Rectangle = { kind: "rectangle"; width: number; height: number };
type Circle = { kind: "circle"; radius: number };

type Shape = Square | Rectangle | Circle;

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "square":
      return shape.size * shape.size;
    case "rectangle":
      return shape.width * shape.height;
    case "circle":
      return Math.PI * shape.radius * shape.radius;
  }
}

let square: Square = { kind: "square", size: 10 };
let circle: Circle = { kind: "circle", radius: 5 };

console.log(getArea(square));  // 100
console.log(getArea(circle));  // 78.53981633974483

この例では、Shape 型として SquareRectangleCircle の3つの異なる型をユニオン型で定義しています。getArea 関数では switch 文を使って shape.kind に基づくパターンマッチングを行い、それぞれの図形に応じた面積計算をしています。

パターンマッチングの利点

パターンマッチングとユニオン型の組み合わせには、次のような利点があります。

  • コードの簡潔化: 型ごとに異なる処理をシンプルに書けるため、冗長な条件分岐が不要になります。
  • 型安全性の強化: パターンマッチングにより、TypeScriptの型チェックが強化され、実行時エラーを未然に防ぐことができます。
  • 可読性の向上: 複雑なロジックでも直感的に理解しやすく、メンテナンスがしやすいコードを書くことができます。

ユニオン型とパターンマッチングを効果的に活用することで、複雑なデータ処理でも型安全性を維持しながら効率的に実装できます。

ユニオン型の課題と解決策

ユニオン型は非常に柔軟で便利な機能ですが、使用時にはいくつかの課題に直面することがあります。これらの課題は、特定の状況においてコードの可読性や型安全性に影響を与える可能性があります。しかし、適切な対策を講じることで、これらの問題を克服し、ユニオン型の強力さを維持しつつ実践的に使用することが可能です。

課題1: 型チェックの複雑化

ユニオン型を使用すると、複数の型が混在するため、型チェックが複雑になりがちです。例えば、ユニオン型で定義された関数内で、すべての型に対して異なる処理を行う必要がある場合、コードが長くなり、メンテナンス性が低下することがあります。

function processValue(value: string | number | boolean) {
  if (typeof value === "string") {
    console.log("String value:", value);
  } else if (typeof value === "number") {
    console.log("Number value:", value);
  } else if (typeof value === "boolean") {
    console.log("Boolean value:", value);
  }
}

このように、ユニオン型のすべての可能性をカバーするために、多数の型チェックを行う必要があります。

解決策: ヘルパー関数の活用

この課題を解決する一つの方法として、ヘルパー関数を用いて型チェックのロジックを簡素化することが考えられます。こうすることで、冗長なコードを減らし、コードの再利用性を高めることができます。

function isString(value: any): value is string {
  return typeof value === "string";
}

function processValue(value: string | number | boolean) {
  if (isString(value)) {
    console.log("String value:", value);
  } else if (typeof value === "number") {
    console.log("Number value:", value);
  } else {
    console.log("Boolean value:", value);
  }
}

ヘルパー関数を使うことで、型チェックが整理され、コードの読みやすさが向上します。

課題2: 型推論の難しさ

ユニオン型を使用すると、特に複雑な型が絡む場合、TypeScriptの型推論が困難になることがあります。これは、特定のユニオン型の一部に対して操作を行いたい場合、型推論が正確に行われない場合があるためです。

let value: string | number = "Hello";
// TypeScriptはこの時点でユニオン型を持っているため、特定の操作が制限される場合がある
value.toUpperCase();  // エラー

この例では、valuestring であると明示しているにもかかわらず、ユニオン型のために toUpperCase() を直接呼び出すことができません。

解決策: 型ガードの活用

この問題を解決するために、TypeScriptの「型ガード」を活用します。型ガードは、条件分岐によって特定の型であることを保証し、TypeScriptに正しい型情報を伝える手法です。

function processValue(value: string | number) {
  if (typeof value === "string") {
    console.log(value.toUpperCase());  // 型ガードにより、安全にメソッドを使用できる
  } else {
    console.log(value.toFixed(2));  // number 型に対する処理
  }
}

このように型ガードを用いることで、TypeScriptの型推論が正確に機能し、型に応じた適切な処理が可能になります。

課題3: ユニオン型の冗長さ

ユニオン型の使用は、柔軟さをもたらしますが、複数の型が含まれる場合、型の定義が冗長になることがあります。特に、複数の型の組み合わせや入れ子構造が複雑化すると、コードが読みにくくなる可能性があります。

解決策: 型エイリアスの活用

型エイリアス(type)を活用することで、複雑なユニオン型の定義をシンプルに整理し、コードの可読性を保つことができます。

type StringOrNumber = string | number;

function printValue(value: StringOrNumber) {
  if (typeof value === "string") {
    console.log("String:", value);
  } else {
    console.log("Number:", value);
  }
}

型エイリアスを使うことで、複雑な型を簡潔に表現でき、再利用性も高まります。

まとめ

ユニオン型には、型チェックの複雑さや型推論の問題などの課題がありますが、型ガードやヘルパー関数、型エイリアスなどの工夫を取り入れることで、これらの課題を効果的に解決できます。適切な方法を用いれば、ユニオン型の柔軟性を保ちながら型安全なコードを維持することが可能です。

交差型の複雑さと対処法

交差型は、複数の型を組み合わせることで強力な型定義を可能にしますが、複雑な型が絡む場合、管理や利用が難しくなることがあります。交差型を使いこなすためには、その複雑さに対処するための工夫が必要です。ここでは、交差型の複雑さに関連する課題と、それに対処する方法を解説します。

課題1: 型の競合

交差型では、異なる型のプロパティが重複する場合、型の競合が発生する可能性があります。特に、同名のプロパティを持つ複数の型を交差させると、TypeScriptがどのプロパティを参照すべきかが不明確になる場合があります。

type A = { name: string; value: number };
type B = { name: string; value: string };

type AB = A & B;

let ab: AB = {
  name: "Conflict",  // OK
  value: 42          // エラー: 型 'number' は 'string' に割り当てられません
};

この例では、AB の両方が namevalue プロパティを持っていますが、value の型が numberstring で競合しています。そのため、TypeScriptは value の型を判別できず、エラーが発生します。

解決策: プロパティの型を明示的に管理

この課題を解決するためには、型定義を整理し、競合が起きないようにプロパティの型を明示的に管理する必要があります。交差型の中で、同名のプロパティが異なる型を持つ場合は、型定義を見直すか、プロパティの型を統一することが重要です。

type A = { name: string; value: number };
type B = { name: string; description: string };

type AB = A & B;

let ab: AB = {
  name: "Resolved",
  value: 42,
  description: "This is a description"
};

このように、競合する型を解消することで、交差型を正しく利用できるようになります。

課題2: ネストされた型の複雑化

交差型を使用して複数の型を組み合わせると、特に入れ子構造が複雑な場合、型が非常に複雑化し、可読性やメンテナンス性が低下することがあります。複数の交差型を組み合わせると、意図しない型の構造が生まれることもあります。

type Person = { name: string; age: number };
type Employee = { company: string; position: string };

type FullTimeEmployee = Person & Employee & { salary: number };

let employee: FullTimeEmployee = {
  name: "John",
  age: 30,
  company: "ABC Corp",
  position: "Developer",
  salary: 50000
};

この例は比較的単純な交差型の使用例ですが、さらに複雑な型の組み合わせを行うと、型の定義が冗長になり、メンテナンスが困難になります。

解決策: 型エイリアスの活用とリファクタリング

型エイリアスを使って、複雑な交差型の定義を整理し、再利用可能な型定義を作成することで、可読性を保ちつつ複雑さを軽減できます。また、必要に応じて型定義をリファクタリングし、分かりやすくすることも重要です。

type Person = { name: string; age: number };
type Employee = { company: string; position: string };
type Salary = { salary: number };

type FullTimeEmployee = Person & Employee & Salary;

let employee: FullTimeEmployee = {
  name: "Jane",
  age: 28,
  company: "XYZ Inc",
  position: "Manager",
  salary: 70000
};

型エイリアスを適切に使うことで、コード全体が見やすくなり、将来的な変更に対しても柔軟に対応できるようになります。

課題3: 予期しない型拡張

交差型を使う際に、すべてのプロパティが結合されるため、意図せずに不要なプロパティが型に追加されてしまうことがあります。このような場合、交差型が拡張されすぎて、必要以上に複雑な型が作られてしまう可能性があります。

解決策: 型のスコープを適切に制限

交差型を定義する際には、型のスコープを適切に制限し、必要な型だけを組み合わせることが重要です。不要な型を組み合わせないように、最小限のプロパティを持つ型を定義することで、複雑さを抑えることができます。

type ContactInfo = { email: string; phone?: string };
type Address = { city: string; country: string };

type UserProfile = ContactInfo & Address;

let user: UserProfile = {
  email: "jane@example.com",
  city: "New York",
  country: "USA"
};

このように、交差型の範囲を適切に制限することで、予期しない型拡張を防ぎ、シンプルで管理しやすい型定義を保つことができます。

まとめ

交差型は、異なる型を組み合わせて強力な型定義を行うための便利な機能ですが、複雑さや競合が生じることがあります。これらの課題に対処するためには、プロパティの競合を避け、型エイリアスや型ガードを活用することで、型の管理を簡潔にし、コードの可読性やメンテナンス性を向上させることができます。適切な設計を行うことで、交差型のメリットを最大限に活かせます。

ユニオン型・交差型を組み合わせた高度な例

TypeScriptでは、ユニオン型と交差型を組み合わせることで、さらに強力で柔軟な型定義を行うことができます。これにより、複雑なデータ構造や多様なケースに対応するロジックを構築でき、現実のシステムに即した設計が可能になります。ここでは、ユニオン型と交差型を併用した高度な使用例を紹介します。

ケース1: 複雑なユーザー権限システム

例えば、ユーザー権限を管理するシステムでは、異なる権限レベル(管理者、編集者、閲覧者)を持つユーザーが存在し、それぞれ異なる属性や操作権限を持っています。この場合、ユニオン型と交差型を組み合わせて、柔軟にユーザーの権限を管理できます。

type BaseUser = {
  id: number;
  name: string;
};

type AdminPrivileges = {
  manageUsers: boolean;
};

type EditorPrivileges = {
  editContent: boolean;
};

type ViewerPrivileges = {
  viewContent: boolean;
};

type AdminUser = BaseUser & AdminPrivileges;
type EditorUser = BaseUser & EditorPrivileges;
type ViewerUser = BaseUser & ViewerPrivileges;

type User = AdminUser | EditorUser | ViewerUser;

function handleUser(user: User) {
  console.log(`User: ${user.name}`);
  if ('manageUsers' in user) {
    console.log("Admin privileges: can manage users");
  } else if ('editContent' in user) {
    console.log("Editor privileges: can edit content");
  } else if ('viewContent' in user) {
    console.log("Viewer privileges: can view content");
  }
}

const admin: AdminUser = { id: 1, name: "Alice", manageUsers: true };
const editor: EditorUser = { id: 2, name: "Bob", editContent: true };
const viewer: ViewerUser = { id: 3, name: "Charlie", viewContent: true };

handleUser(admin);  // Admin privileges
handleUser(editor);  // Editor privileges
handleUser(viewer);  // Viewer privileges

この例では、User 型をユニオン型として定義し、それぞれのユーザー権限(AdminUserEditorUserViewerUser)を交差型で構成しています。これにより、各権限に応じた処理が行え、型安全性を保ちながら柔軟なロジックを実装できます。

ケース2: 複数のオプションを持つAPIのレスポンス

APIレスポンスにおいて、異なるデータ形式が返されることがあります。例えば、APIの成功時とエラー時のレスポンスが異なる場合、ユニオン型と交差型を組み合わせることで、効率的に処理できます。

type ApiResponseSuccess = {
  status: "success";
  data: {
    id: number;
    content: string;
  };
};

type ApiResponseError = {
  status: "error";
  message: string;
};

type ApiResponse = ApiResponseSuccess | ApiResponseError;

function handleApiResponse(response: ApiResponse) {
  if (response.status === "success") {
    console.log("Data:", response.data.content);
  } else {
    console.error("Error:", response.message);
  }
}

const successResponse: ApiResponseSuccess = {
  status: "success",
  data: { id: 1, content: "Hello World" }
};

const errorResponse: ApiResponseError = {
  status: "error",
  message: "Something went wrong"
};

handleApiResponse(successResponse);  // Logs "Data: Hello World"
handleApiResponse(errorResponse);    // Logs "Error: Something went wrong"

この例では、APIレスポンスをユニオン型として定義し、成功時とエラー時のそれぞれの型に応じた処理を行っています。これにより、どのようなレスポンスが返ってきても安全に処理を実行でき、エラーケースにも柔軟に対応できます。

ケース3: 複数の型を持つフォームデータ

フォーム入力データを扱う際、異なるフィールドが組み合わさって構成される場合があります。このようなケースでは、ユニオン型と交差型を併用して、動的に構成されるフォームデータの型を定義できます。

type TextField = {
  type: "text";
  value: string;
};

type NumberField = {
  type: "number";
  value: number;
};

type BooleanField = {
  type: "checkbox";
  checked: boolean;
};

type FormField = TextField | NumberField | BooleanField;

function handleFormField(field: FormField) {
  if (field.type === "text") {
    console.log("Text input:", field.value);
  } else if (field.type === "number") {
    console.log("Number input:", field.value);
  } else if (field.type === "checkbox") {
    console.log("Checkbox is", field.checked ? "checked" : "unchecked");
  }
}

const textField: TextField = { type: "text", value: "Hello" };
const numberField: NumberField = { type: "number", value: 42 };
const booleanField: BooleanField = { type: "checkbox", checked: true };

handleFormField(textField);  // "Text input: Hello"
handleFormField(numberField);  // "Number input: 42"
handleFormField(booleanField);  // "Checkbox is checked"

ここでは、ユニオン型を用いて複数のフォームフィールドを定義し、それぞれの型に応じた処理を行っています。この設計により、異なる入力形式を柔軟に処理できるフォームシステムを構築できます。

まとめ

ユニオン型と交差型を組み合わせることで、TypeScriptにおける型の柔軟性がさらに向上し、複雑なデータ構造を型安全に処理できるようになります。これらの高度な使い方を習得することで、実践的なシステム設計が可能となり、より堅牢で保守しやすいコードを構築することができます。

まとめ

本記事では、TypeScriptにおけるユニオン型と交差型の基本から、実践的な使用例や課題への対処法、さらにそれらを組み合わせた高度な使い方について解説しました。ユニオン型は異なる型を柔軟に扱うために、交差型は複数の型を結合して強力な型定義を作成するために利用されます。これらを効果的に活用することで、型安全なコードを実現し、複雑なデータ構造やロジックを効率的に管理することができます。実際の開発でこれらの型を活用することで、より堅牢でメンテナンス性の高いシステムを構築できるでしょう。

コメント

コメントする

目次