TypeScriptでユニオン型を安全に扱う実践例(switch文など)
生徒
「TypeScriptでユニオン型を使うと便利って聞いたのですが、安全に扱う方法がよく分かりません…。」
先生
「ユニオン型は、ひとつの変数に複数の型が入る可能性を表すTypeScriptの基本的な考え方です。安全に使うには、switch文などを活用して、どの型が入っているのかを丁寧に判断する必要があります。」
生徒
「switch文で型の判定ができるんですか?具体的な例で教えてほしいです!」
先生
「もちろんです。実際のプログラムでどう使うのか、順番に見ていきましょう。」
1. ユニオン型とは?
TypeScriptのユニオン型とは、ひとつの変数が複数の型のどれかになり得る状態を表す型です。例えば、文字列か数値のどちらかが入るようにしたいとき、string | numberのように書きます。これは日常の「AかBのどちらか」という考え方に近く、とても自然な書き方です。
ユニオン型を使うと型の柔軟性が増しますが、その分「実際に今どの型が入っているのか」を判断しながら安全に処理を書く必要が出てきます。そのために役立つのが、switch文を使った型ガードです。
2. ユニオン型をswitch文で安全に扱う理由
ユニオン型の変数には複数の型が入る可能性があります。そのまま使うと、想定外の型が入っていた場合にエラーが起きることがあります。そこで、switch文などで型を振り分けることで、TypeScriptに「この分岐ではこの型である」と教えてあげます。こうすることで、型の安全性が守られ、間違った処理を避けることができます。
特にリテラル型(決まった文字だけを型として扱うもの)と組み合わせると、switch文が非常に強力に働きます。分岐の書き方が明確になり、コードも読みやすくなります。
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. 数値と文字列が混ざるユニオン型を安全に使う
次に、「数値か文字列」のどちらかが入るケースです。この場合は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. 実践的なユニオン型のパターン:オブジェクトの種類を判定する
オブジェクト型のユニオンを扱う場面は、実際の開発で非常に多くあります。ここでは、共通のプロパティとして「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文でユニオン型を扱うと読みやすいコードになる理由
switch文は、複数の値を明確に並べて記述できるため、ソースコードを読む人にとって理解しやすい形式になります。また、各ケースごとに具体的な処理を書けるので、ユニオン型との相性が非常に良いのです。
特に初心者の段階では、if文よりもswitch文のほうが「どの値に対して何をするか」が一覧で確認しやすく、プログラムの動きを理解しやすくなります。