TypeScript PR

【TypeScript】ユニオン(Union)型の判定方法!typeof・in・instanceofを使い分けよう

記事内に商品プロモーションを含む場合があります

こんにちは!

TypeScriptでプログラミングをしていると、ユニオン型の判定について悩むことがありませんか?

ユニオン型って何だろう?型の判定ってどうやるの?
string | number型の変数で、どちらの型かを判定したい…
型ガードって聞いたことあるけど、実際の使い方がわからない

TypeScriptでの型安全なコード開発において、このような疑問をお持ちではないでしょうか。

この記事では、TypeScriptの「ユニオン型判定」について、基本概念から具体的な判定方法、実践的な型ガードの使い方までを詳しく解説します。

この記事は次のような方におすすめです。

この記事はこんな人におすすめ!
  • TypeScriptのユニオン型について理解を深めたい方
  • 型ガードを使った安全な型判定を学びたい方
  • 実際のプロジェクトで使える型判定テクニックを知りたい開発者
  • TypeScriptの型システムを効果的に活用したい方
  • コードの型安全性を向上させたい方

この記事を読めば、ユニオン型判定の仕組みが理解でき、実際のプロジェクトですぐに活用できるスキルが身につきます。TypeScriptの型システムを活用して、より安全で保守性の高いコードを書けるようになりましょう。

「型安全なコードを書きたい方」「TypeScriptの機能を効果的に活用したい方」は、ぜひ参考にしてください。

それでは、順を追って詳しく見ていきましょう!

TypeScriptのユニオン型とは何か

TypeScriptのユニオン型(Union Type)は、複数の型のうちのいずれかになる可能性を表現する型です。パイプ記号(|)を使用して複数の型を結合し、変数や関数の引数が複数の型のいずれかを受け入れることを示します。

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

  • 複数の型をパイプ記号(|)で繋げて定義します。
  • 変数は定義された型のいずれかになることができます。
  • 型安全性を保ちながら柔軟な値の受け渡しが可能になります。
  • コンパイル時に型チェックが行われるため、実行時エラーを事前に防げます。
// ユニオン型の基本的な例
let value: string | number;
value = "こんにちは"; // string型として有効
value = 42; // number型として有効
// value = true; // エラー:booleanは許可されていません

この例では、value変数は文字列または数値のいずれかを保持できますが、真偽値を代入しようとするとコンパイルエラーが発生します。

TypeScriptの型判定(型ガード)とは

型ガード(Type Guard)は、条件分岐を使用してユニオン型の変数がどの型であるかを判定し、その型に応じて処理を分岐させる仕組みです。TypeScriptは条件分岐の結果を分析し、各ブロック内で変数の型を自動的に絞り込みます。

型ガードを使用することで、以下のようなメリットが得られます。

  • ユニオン型の変数を安全に操作できます。
  • 各型固有のプロパティやメソッドにアクセス可能になります。
  • コンパイル時に型エラーを検出できます。
  • 実行時の予期しないエラーを防げます。
function processValue(value: string | number) {
  // この時点では、valueはstring | number型
  // value.toUpperCase(); // エラー:numberにはtoUpperCaseメソッドがない

  if (typeof value === 'string') {
    // この分岐内では、valueはstring型として扱われる
    console.log(value.toUpperCase()); // 正常に動作
  } else {
    // この分岐内では、valueはnumber型として扱われる
    console.log(value.toFixed(2)); // 正常に動作
  }
}

typeof演算子による型判定

typeof演算子は、プリミティブ型のユニオン型を判定するための最も基本的な型ガードです。JavaScriptの標準的なtypeof演算子を使用して、値の型を文字列として取得し、その結果に基づいて型を絞り込みます。

typeof演算子が返す文字列は以下の通りです。

typeof演算子の戻り値
string “string”
number “number”
boolean “boolean”
undefined “undefined”
symbol “symbol”
bigint “bigint”
object(nullを含む) “object”
function “function”

注意すべき点として、nullの場合も”object”が返されることを覚えておきましょう。

function formatValue(value: string | number | boolean): string {
  if (typeof value === 'string') {
    return `文字列: ${value.toUpperCase()}`;
  } else if (typeof value === 'number') {
    return `数値: ${value.toFixed(2)}`;
  } else if (typeof value === 'boolean') {
    return `真偽値: ${value ? 'はい' : 'いいえ'}`;
  }
  return '不明な型です';
}

console.log(formatValue("hello")); // "文字列: HELLO"
console.log(formatValue(123.456)); // "数値: 123.46"
console.log(formatValue(true)); // "真偽値: はい"

この例では、引数の型に応じて異なる処理を行い、それぞれの型固有のメソッドを安全に使用しています。

instanceof演算子による型判定

instanceof演算子は、クラスのインスタンスやオブジェクトの型を判定するための型ガードです。オブジェクトが特定のクラスのインスタンスであるかどうかをチェックし、その結果に基づいて型を絞り込みます。

class Dog {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  bark(): string {
    return "ワンワン!";
  }
}

class Cat {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  meow(): string {
    return "ニャー!";
  }
}

function makeSound(animal: Dog | Cat): string {
  if (animal instanceof Dog) {
    // この分岐内では、animalはDog型として扱われる
    return `${animal.name}が${animal.bark()}`;
  } else {
    // この分岐内では、animalはCat型として扱われる
    return `${animal.name}が${animal.meow()}`;
  }
}

const myDog = new Dog("ポチ");
const myCat = new Cat("タマ");

console.log(makeSound(myDog)); // "ポチがワンワン!"
console.log(makeSound(myCat)); // "タマがニャー!"

instanceof演算子は、組み込みオブジェクトの判定にも使用できます。

function processData(data: string | Date): string {
  if (data instanceof Date) {
    return `日付: ${data.toLocaleDateString()}`;
  } else {
    return `文字列: ${data}`;
  }
}

console.log(processData("今日は良い天気")); // "文字列: 今日は良い天気"
console.log(processData(new Date())); // "日付: 2025/1/15" (現在の日付)

in演算子による型判定

in演算子は、オブジェクトが特定のプロパティを持っているかどうかを判定する型ガードです。オブジェクト型のユニオン型において、各型が持つ固有のプロパティの存在をチェックして型を絞り込みます。

type Fish = {
  name: string;
  swim(): void;
}

type Bird = {
  name: string;
  fly(): void;
}

function moveAnimal(animal: Fish | Bird): void {
  if ('swim' in animal) {
    // この分岐内では、animalはFish型として扱われる
    console.log(`${animal.name}が泳いでいます`);
    animal.swim();
  } else {
    // この分岐内では、animalはBird型として扱われる
    console.log(`${animal.name}が飛んでいます`);
    animal.fly();
  }
}

const fish: Fish = {
  name: "金魚",
  swim() { console.log("スイスイ泳ぎます"); }
};

const bird: Bird = {
  name: "スズメ",
  fly() { console.log("パタパタ飛びます"); }
};

moveAnimal(fish); // "金魚が泳いでいます" "スイスイ泳ぎます"
moveAnimal(bird); // "スズメが飛んでいます" "パタパタ飛びます"

in演算子は、オプショナルプロパティの判定にも使用できます。

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

function getContactInfo(user: User): string {
  let contact = `名前: ${user.name}`;

  if ('email' in user && user.email) {
    contact += `、メール: ${user.email}`;
  }

  if ('phone' in user && user.phone) {
    contact += `、電話: ${user.phone}`;
  }

  return contact;
}

ユーザー定義型ガード

ユーザー定義型ガードは、カスタムの関数を作成して、より複雑な型判定を行う手法です。関数の戻り値として「引数 is 型」という型述語(Type Predicate)を使用し、判定結果に基づいて型を絞り込みます。

type Rectangle = {
  type: 'rectangle';
  width: number;
  height: number;
}

type Circle = {
  type: 'circle';
  radius: number;
}

type Shape = Rectangle | Circle;

// ユーザー定義型ガード関数
function isRectangle(shape: Shape): shape is Rectangle {
  return shape.type === 'rectangle';
}

function isCircle(shape: Shape): shape is Circle {
  return shape.type === 'circle';
}

function calculateArea(shape: Shape): number {
  if (isRectangle(shape)) {
    // この分岐内では、shapeはRectangle型として扱われる
    return shape.width * shape.height;
  } else if (isCircle(shape)) {
    // この分岐内では、shapeはCircle型として扱われる
    return Math.PI * shape.radius * shape.radius;
  }

  return 0;
}

const rect: Rectangle = { type: 'rectangle', width: 10, height: 5 };
const circle: Circle = { type: 'circle', radius: 3 };

console.log(calculateArea(rect)); // 50
console.log(calculateArea(circle)); // 約28.27

ユーザー定義型ガードは、複数の条件を組み合わせた複雑な判定にも使用できます。

type AdminUser = {
  name: string;
  role: 'admin';
  permissions: string[];
}

type RegularUser = {
  name: string;
  role: 'user';
}

type User = AdminUser | RegularUser;

function isAdminUser(user: User): user is AdminUser {
  return user.role === 'admin' && 'permissions' in user;
}

function displayUserInfo(user: User): string {
  if (isAdminUser(user)) {
    return `管理者: ${user.name} (権限: ${user.permissions.join(', ')})`;
  } else {
    return `一般ユーザー: ${user.name}`;
  }
}

判別可能なユニオン型

判別可能なユニオン型(Discriminated Union)は、各型が共通のプロパティを持ち、そのプロパティの値によって型を区別できる特殊なユニオン型です。この手法を使用することで、より安全で読みやすい型判定が可能になります。

type LoadingState = {
  status: 'loading';
}

type SuccessState = {
  status: 'success';
  data: string[];
}

type ErrorState = {
  status: 'error';
  message: string;
}

type AppState = LoadingState | SuccessState | ErrorState;

function handleAppState(state: AppState): string {
  switch (state.status) {
    case 'loading':
      // この分岐内では、stateはLoadingState型
      return "データを読み込み中...";

    case 'success':
      // この分岐内では、stateはSuccessState型
      return `データを${state.data.length}件取得しました`;

    case 'error':
      // この分岐内では、stateはErrorState型
      return `エラーが発生しました: ${state.message}`;

    default:
      // TypeScriptが全ての場合を網羅していることを確認
      const _exhaustive: never = state;
      return _exhaustive;
  }
}

// 使用例
const loadingState: LoadingState = { status: 'loading' };
const successState: SuccessState = { status: 'success', data: ['item1', 'item2'] };
const errorState: ErrorState = { status: 'error', message: 'ネットワークエラー' };

console.log(handleAppState(loadingState)); // "データを読み込み中..."
console.log(handleAppState(successState)); // "データを2件取得しました"
console.log(handleAppState(errorState)); // "エラーが発生しました: ネットワークエラー"

判別可能なユニオン型は、状態管理やAPIレスポンスの型定義によく使用されます。

型判定手法の使い分け

TypeScriptには複数の型判定手法がありますが、それぞれ適切な使い分けが重要です。以下の表で、各手法の特徴と適用場面を比較してみましょう。

型判定手法 適用対象 特徴 使用場面
typeof プリミティブ型 簡潔で高速 string、number、booleanなどの基本型
instanceof クラスインスタンス オブジェクトの継承関係を考慮 クラスベースのオブジェクト判定
in オブジェクトプロパティ プロパティの存在チェック 異なる構造を持つオブジェクト型
ユーザー定義型ガード カスタム判定 複雑な条件に対応 独自のビジネスロジックを含む判定
判別可能なユニオン型 共通プロパティ持つ型 安全で読みやすい 状態管理、APIレスポンス型

適切な手法を選択することで、コードの可読性と保守性が向上します。単純な型判定にはtypeofを使用し、複雑な判定が必要な場合はユーザー定義型ガードや判別可能なユニオン型を検討しましょう。

TypeScript 4.4以降の型ガード変数

TypeScript 4.4以降では、型ガードの結果を変数に代入して再利用することができるようになりました。この機能により、同じ型判定を複数回行う必要がある場合のコードをより簡潔に書けます。

function processDate(input: string | Date): string {
  const isDate = input instanceof Date;

  if (isDate) {
    // この分岐内では、inputはDate型として扱われる
    return `日付: ${input.getFullYear()}年${input.getMonth() + 1}月`;
  } else {
    // この分岐内では、inputはstring型として扱われる
    return `文字列: ${input}`;
  }
}

function validateInput(value: unknown): boolean {
  const isString = typeof value === 'string';
  const isNumber = typeof value === 'number';

  if (isString) {
    return value.length > 0; // valueはstring型として扱われる
  } else if (isNumber) {
    return value > 0; // valueはnumber型として扱われる
  }

  return false;
}

この機能は、特に複雑な条件判定を行う場合にコードの重複を減らし、可読性を向上させます。

【付録】さらに学びを深めるためのリソース


さらにTypescriptの学習を進めたい方のために、いくつかのリソースを紹介します。
これらのリソースを活用することで、TypeScriptの型システムについてより深い知識を得ることができるでしょう。

おすすめの書籍


プロを目指す人のためのTypeScript入門


プロ開発者への近道!基礎から応用まで幅広くカバー

1一番のおすすめは「プロを目指す人のためのTypeScript入門」です。
この本は、TypeScriptの基礎から高度な使い方まで幅広く解説しています。

初心者でも理解しやすい説明から始まり、徐々に難易度を上げていく構成が特徴。
プログラミング経験者なら、さらに深い理解が得られるでしょう。

実際の開発現場で使えるテクニックも豊富に紹介されているのがポイント。
型の使い方や設計パターンなど、実践的な内容が満載です。

プロの開発者を目指す人はもちろん、TypeScriptをしっかり学びたい人におすすめの一冊。
基礎固めから応用力の向上まで、幅広いニーズに応えてくれます。

現場で使えるTypeScript 詳解実践ガイド


実践的スキルを身につけたい人必見!現場のノウハウが詰まった一冊

次におすすめは「現場で使えるTypeScript 詳解実践ガイド」がランクイン。
この本の特徴は、実際の開発現場で役立つ知識が詰まっていること。

TypeScriptの基本的な文法から始まり、実際のプロジェクトでどう活用するかまで丁寧に解説しています。
エラー対処法や性能改善のコツなど、現場ならではの知恵も満載。

特に、大規模なプロジェクトでTypeScriptを使う際のベストプラクティスが学べるのが魅力。
コードの保守性や再利用性を高める方法も詳しく紹介されています。

すでにJavaScriptの経験がある人や、実務でTypeScriptを使いたい人におすすめ。
この本を読めば、現場で即戦力として活躍できる力が身につくはずです。

書籍に関してはこちらの記事も参考にしてくださいね!




オンラインで参照できる公式ドキュメント


TypeScript公式ハンドブック


https://www.typescriptlang.org/docs/
TypeScriptの公式ドキュメントです。
intersection型を含む、すべての型システムの機能について詳細な説明があります。

TypeScript Deep Dive


https://basarat.gitbook.io/typescript/
TypeScriptの深い部分まで掘り下げて解説しているオンラインブックです。
無料で読むことができ、intersection型についても詳しく説明されています。

TypeScriptの学習は終わりがありません。
新しい機能が常に追加され、より良い書き方が発見されています。
継続的に学習を続けることで、より良いTypeScriptプログラマーになれるはずです。

キャリア形成/給与還元
ひとつひとつ真摯に向き合う企業
ONE_WEDGE社員募集

株式会社 ONE WEDGEでは、新たな仲間を募集しています!

私たちと一緒に、革新的で充実したキャリアを築きませんか?
当社は、従業員が仕事と私生活のバランスを大切にできるよう、充実した福利厚生を整えています。

  • 完全週休2日制(土日休み)で、祝日や夏季休暇、年末年始休暇もしっかり保証!
  • 様々な休暇制度(有給、慶弔、産前・産後、育児、バースデー休暇、有給6日取得で特別休暇付与)を完備!
  • 従業員の成長と健康を支援するための表彰制度、資格取得支援、健康促進手当など!
  • 生活を支えるテレワーク手当、記事寄稿手当、結婚祝金・出産祝金など、様々な手当を提供!
  • 自己啓発としての書籍購入制度や、メンバー間のコミュニケーションを深める交流費補助!
  • 成果に応じた決算賞与や、リファラル採用手当、AI手当など、頑張りをしっかり評価!
  • ワークライフバランスを重視し、副業もOK!

株式会社 ONE WEDGEでは、一人ひとりの従業員が自己実現できる環境を大切にしています。
共に成長し、刺激を与え合える仲間をお待ちしております。
あなたの能力と熱意を、ぜひ当社で発揮してください。
ご応募お待ちしております!

ホームページ、採用情報は下記ボタンからご確認ください!

応募、ご質問など、お問い合わせフォーム、またはX (旧Twitter)、InstagramのDMでお気軽にご相談ください♪

まとめ

TypeScriptのユニオン型判定は、型安全なコードを書くための重要な技術です。適切な型ガードを使用することで、コンパイル時のエラー検出と実行時の安全性を両立できます。

  • typeof演算子はプリミティブ型の判定に最適で、シンプルで高速な処理が可能です。
  • instanceof演算子はクラスインスタンスの判定に使用し、継承関係も考慮されます。
  • in演算子はオブジェクトプロパティの存在チェックに使用し、構造の異なる型の判定に有効です。
  • ユーザー定義型ガードはカスタムロジックによる複雑な判定を実現できます。
  • 判別可能なユニオン型は共通プロパティを使用した安全で読みやすい型判定を提供します。

適切な型判定手法を選択することで、TypeScriptの型システムを最大限に活用し、保守性の高いコードを作成できます。各手法の特徴を理解し、プロジェクトの要件に応じて使い分けることが重要です。

TypeScriptの型システムを活用して、より安全で効率的な開発を進めていきましょう。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です