カテゴリ: TypeScript 更新日: 2025/12/12

TypeScriptでユニオン型を安全に扱う実践例(switch文など)

TypeScriptでユニオン型を安全に扱う実践例(switch文など)
TypeScriptでユニオン型を安全に扱う実践例(switch文など)

先生と生徒の会話形式で理解しよう

生徒

「TypeScriptでユニオン型を使うと便利って聞いたのですが、安全に扱う方法がよく分かりません…。」

先生

「ユニオン型は、ひとつの変数に複数の型が入る可能性を表すTypeScriptの基本的な考え方です。安全に使うには、switch文などを活用して、どの型が入っているのかを丁寧に判断する必要があります。」

生徒

「switch文で型の判定ができるんですか?具体的な例で教えてほしいです!」

先生

「もちろんです。実際のプログラムでどう使うのか、順番に見ていきましょう。」

1. ユニオン型とは?

1. ユニオン型とは?
1. ユニオン型とは?

TypeScriptのユニオン型とは、ひとつの変数が複数の型のどれかになり得る状態を表す型です。例えば、文字列か数値のどちらかが入るようにしたいとき、string | numberのように書きます。これは日常の「AかBのどちらか」という考え方に近く、とても自然な書き方です。

ユニオン型を使うと型の柔軟性が増しますが、その分「実際に今どの型が入っているのか」を判断しながら安全に処理を書く必要が出てきます。そのために役立つのが、switch文を使った型ガードです。

2. ユニオン型をswitch文で安全に扱う理由

2. ユニオン型をswitch文で安全に扱う理由
2. ユニオン型をswitch文で安全に扱う理由

ユニオン型の変数には複数の型が入る可能性があります。そのまま使うと、想定外の型が入っていた場合にエラーが起きることがあります。そこで、switch文などで型を振り分けることで、TypeScriptに「この分岐ではこの型である」と教えてあげます。こうすることで、型の安全性が守られ、間違った処理を避けることができます。

特にリテラル型(決まった文字だけを型として扱うもの)と組み合わせると、switch文が非常に強力に働きます。分岐の書き方が明確になり、コードも読みやすくなります。

3. 具体例:文字列のリテラル型で動作を分ける

3. 具体例:文字列のリテラル型で動作を分ける
3. 具体例:文字列のリテラル型で動作を分ける

まずは、"start" | "stop" | "pause"のようなリテラル型を使った例です。それぞれの状態によって実行する処理を変えたい場合、switch文が最もシンプルで確実です。


type Command = "start" | "stop" | "pause";

function handleCommand(cmd: Command) {
    switch (cmd) {
        case "start":
            console.log("スタートしました");
            break;
        case "stop":
            console.log("停止しました");
            break;
        case "pause":
            console.log("一時停止しました");
            break;
        default:
            // ユニオン型なので通常ここには来ない
            const neverValue: never = cmd;
    }
}

handleCommand("start");

スタートしました

リテラル型を使うと選択肢が限定されるため、switch文で安心して分岐できます。defaultにnever型を入れることで、想定外の値が入っていないかをTypeScriptに確認させることもできます。

4. 数値と文字列が混ざるユニオン型を安全に使う

4. 数値と文字列が混ざるユニオン型を安全に使う
4. 数値と文字列が混ざるユニオン型を安全に使う

次に、「数値か文字列」のどちらかが入るケースです。この場合はtypeofを使って判断する方法がよく使われますが、switch文でも明確に分岐させることができます。


type Value = number | string;

function printValue(value: Value) {
    switch (typeof value) {
        case "number":
            console.log("数値です:" + value);
            break;
        case "string":
            console.log("文字列です:" + value);
            break;
    }
}

printValue(10);
printValue("Hello");

数値です:10
文字列です:Hello

typeofを使うことで、switch文が「型の判定」として働き、プログラムが確実に安全な状態で動作するようになります。

5. 実践的なユニオン型のパターン:オブジェクトの種類を判定する

5. 実践的なユニオン型のパターン:オブジェクトの種類を判定する
5. 実践的なユニオン型のパターン:オブジェクトの種類を判定する

オブジェクト型のユニオンを扱う場面は、実際の開発で非常に多くあります。ここでは、共通のプロパティとして「kind」を持たせ、それをもとにswitch文で分岐する方法を紹介します。これは「判別可能ユニオン」と呼ばれ、TypeScriptが特に得意とする書き方です。


type Dog = { kind: "dog"; bark: () => void };
type Cat = { kind: "cat"; meow: () => void };
type Animal = Dog | Cat;

function speak(animal: Animal) {
    switch (animal.kind) {
        case "dog":
            animal.bark();
            break;
        case "cat":
            animal.meow();
            break;
    }
}

speak({ kind: "dog", bark: () => console.log("ワン!") });

ワン!

このように共通のプロパティを持つユニオン型は、switch文と非常に相性が良く、安全性の高いコードにしやすい特徴があります。

6. switch文でユニオン型を扱うと読みやすいコードになる理由

6. switch文でユニオン型を扱うと読みやすいコードになる理由
6. switch文でユニオン型を扱うと読みやすいコードになる理由

switch文は、複数の値を明確に並べて記述できるため、ソースコードを読む人にとって理解しやすい形式になります。また、各ケースごとに具体的な処理を書けるので、ユニオン型との相性が非常に良いのです。

特に初心者の段階では、if文よりもswitch文のほうが「どの値に対して何をするか」が一覧で確認しやすく、プログラムの動きを理解しやすくなります。

まとめ

まとめ
まとめ

ユニオン型を安全に扱うために理解しておきたい考え方

この記事では、TypeScriptにおけるユニオン型の基本から、switch文を使った安全な扱い方まで、実践的な視点で解説してきました。ユニオン型は「複数の型のどれかが入る可能性がある」という柔軟な型表現を可能にしますが、その反面、現在どの型が入っているのかを正しく判断しなければ、安全な処理は書けません。

そこで重要になるのが、型ガードという考え方です。switch文やtypeof、共通プロパティを使った分岐によって、TypeScriptに「この分岐ではこの型である」と伝えることで、型の絞り込みが行われます。これにより、補完が正しく効き、存在しないプロパティやメソッドを誤って使ってしまうミスを防ぐことができます。

switch文とユニオン型が相性の良い理由

switch文は、あらかじめ取り得る値が分かっている場合に非常に読みやすく、保守性の高いコードを書くことができます。特に、リテラル型や判別可能ユニオンと組み合わせることで、「どんな状態が存在するのか」「それぞれの状態で何が起きるのか」を一覧で把握できるようになります。

また、default句にnever型を使うことで、「想定外の値が存在しないか」をコンパイル時にチェックできる点も大きなメリットです。これにより、将来的にユニオン型に新しい値が追加された場合でも、対応漏れにすぐ気付けるようになります。これは、規模が大きくなるプロジェクトほど効果を発揮する考え方です。

実務を想定したユニオン型とswitch文の総合サンプル


type Result =
    | { status: "success"; data: string }
    | { status: "error"; message: string }
    | { status: "loading" };

function renderResult(result: Result) {
    switch (result.status) {
        case "success":
            console.log("成功:" + result.data);
            break;
        case "error":
            console.log("エラー:" + result.message);
            break;
        case "loading":
            console.log("読み込み中です");
            break;
        default:
            const neverValue: never = result;
    }
}

renderResult({ status: "success", data: "完了しました" });

この例では、判別可能ユニオンとswitch文を組み合わせることで、状態ごとの処理を明確に分けています。statusプロパティによって型が自動的に絞り込まれるため、それぞれのケースで安全にプロパティへアクセスできます。ReactやAPIレスポンス処理など、実務でも頻繁に使われる代表的なパターンです。

このような書き方を身につけることで、TypeScriptのユニオン型を「なんとなく使う」のではなく、「安全に、意図を持って使う」ことができるようになります。結果として、バグが少なく、読みやすいコードにつながっていきます。

先生と生徒の振り返り会話

生徒

「ユニオン型って便利だけど難しそうだと思っていましたが、switch文を使えば整理して書けるんですね。」

先生

「そうですね。特にリテラル型や判別可能ユニオンと組み合わせると、TypeScriptの良さが一番分かりやすく出てきます。」

生徒

「never型を使って、対応漏れをチェックできるのは知りませんでした。将来の変更にも強そうですね。」

先生

「その通りです。TypeScriptは、未来の自分やチームメンバーを助けるための仕組みでもあります。」

生徒

「これからは、ユニオン型を使うときはswitch文で安全に分けることを意識してみます。」

先生

「それができれば十分です。ユニオン型を正しく扱えるようになると、TypeScriptの理解が一段深まりますよ。」

関連記事:
カテゴリの一覧へ
新着記事
New1
TypeScript
TypeScriptでパスエイリアスを設定する方法!baseUrlとpathsでコードをスッキリ整理
New2
JavaScript
JavaScriptのfor文の書き方を初心者向けにやさしく解説
New3
JavaScript
JavaScriptの関数でよくあるエラーとその解決法まとめ
New4
JavaScript
JavaScriptのイベント処理でよくあるエラーとその対処法
人気記事
No.1
Java&Spring記事人気No1
JavaScript
JavaScriptのインストール方法まとめ!Windows・Mac・Linux別にステップ解説
No.2
Java&Spring記事人気No2
JavaScript
JavaScriptのマウスイベントの使い方(click, mouseoverなど)
No.3
Java&Spring記事人気No3
JavaScript
JavaScriptのtoStringとString関数の違いを初心者向けに解説
No.4
Java&Spring記事人気No4
JavaScript
JavaScriptの純粋関数(pure function)と副作用の違いを理解しよう
No.5
Java&Spring記事人気No5
JavaScript
JavaScriptプログラムの実行方法まとめ!ブラウザ・Node.js・コンソールの使い方
No.6
Java&Spring記事人気No6
JavaScript
JavaScriptで文字列をforループで1文字ずつ処理する方法!初心者向け解説
No.7
Java&Spring記事人気No7
TypeScript
TypeScript学習におすすめの無料教材・リファレンスサイト【初心者向け】
No.8
Java&Spring記事人気No8
TypeScript
TypeScriptの始め方:開発環境の構築手順【初心者向け】