TypeScriptのインターフェースでユニオン型を持つフィールドを扱う方法を徹底解説!初心者向け入門ガイド
生徒
「先生、TypeScriptのインターフェースでフィールドに複数の型を持たせることってできますか?」
先生
「はい、できますよ。そのときに使うのがユニオン型です。ユニオン型とは、ある変数が複数の型のどれか一つを持つことができる仕組みです。」
生徒
「ユニオン型って聞いたことありますけど、インターフェースの中でどうやって使うんですか?」
先生
「では、インターフェースでユニオン型を使った具体的な例を見ていきましょう!」
1. ユニオン型とは?
TypeScriptのユニオン型は「または(OR)」の意味を持ちます。例えば、string | number と書くと、その変数は文字列でも数値でもOKというルールになります。これにより、柔軟に型を指定できるようになります。
ユニオン型は日常生活でいうと「学生証または運転免許証で身分証明できる」といった状況と同じです。どちらか一方を持っていれば条件を満たせる、というイメージです。
2. インターフェースでユニオン型を使う基本
インターフェース(interface)はオブジェクトの設計図のようなものです。ここでユニオン型を使えば、あるプロパティが「文字列または数値」など複数の型を受け入れるようにできます。
interface User {
id: number;
name: string;
status: "active" | "inactive"; // ユニオン型(リテラル型との組み合わせ)
age: number | string; // 数値または文字列
}
const user1: User = {
id: 1,
name: "Taro",
status: "active",
age: 25
};
const user2: User = {
id: 2,
name: "Hanako",
status: "inactive",
age: "不明"
};
{ id: 1, name: "Taro", status: "active", age: 25 }
{ id: 2, name: "Hanako", status: "inactive", age: "不明" }
このように書くことで、ageは数値でも文字列でも代入できるようになります。現実的には「年齢が不明な場合は '不明' と文字列で入れる」といった場面で便利です。
3. ユニオン型とインターフェースの応用
さらに一歩進んで、インターフェースの中にユニオン型を持たせることで、フィールドの値を限定することも可能です。
interface Payment {
method: "credit" | "cash" | "bank"; // 支払い方法を限定
amount: number;
}
const payment1: Payment = {
method: "credit",
amount: 5000
};
const payment2: Payment = {
method: "cash",
amount: 2000
};
{ method: "credit", amount: 5000 }
{ method: "cash", amount: 2000 }
この例では、method に指定できるのは "credit"、"cash"、"bank" のいずれかです。それ以外の文字列を代入するとエラーになります。これにより、プログラムの安全性が高まります。
4. 日常生活の例で考えるユニオン型
ユニオン型を理解するには、生活の例えで考えるのがわかりやすいです。
- レストランの注文で「ご飯またはパン」を選べる →
"rice" | "bread" - 支払い方法で「現金またはクレジットカードまたはQRコード」 →
"cash" | "credit" | "qr" - 問い合わせの回答が「OK または NG」 →
"OK" | "NG"
このように「いくつかの候補から選べる」ものをインターフェースのフィールドにしたいときに、ユニオン型が非常に役立ちます。
5. ユニオン型を使うときの注意点
ユニオン型は便利ですが、使いすぎると「このプロパティは結局どんな型なのか?」が分かりづらくなります。そのため、必要な場面に絞って使うのが大切です。
特に複数の型を許可するときには、あとで値を使う場面で型ガード(どの型なのかを判別する仕組み)を使う必要が出てくることがあります。初心者の方はまず「リテラル型との組み合わせ」や「数値または文字列」くらいから練習するのがおすすめです。
6. ユニオン型と型ガードを組み合わせて安全に扱う
ユニオン型を使うと便利な反面、「実際に値を使うときにどの型として扱えばよいか」を判断する必要があります。そこで登場するのが型ガードです。型ガードを使えば、「今扱っている値が数値なのか文字列なのか」を安全に判定し、適切な処理を行えます。
function showAge(age: number | string): void {
if (typeof age === "number") {
console.log("年齢は " + age + " 歳です");
} else {
console.log("年齢情報: " + age);
}
}
showAge(20);
showAge("不明");
年齢は 20 歳です
年齢情報: 不明
ユニオン型を扱うときは、このように型ガードを使うことで誤動作を防ぎ、安全にデータを処理できます。特に実務では「数値か文字列かわからないが、どちらかは必ず入る」といった場面が多いため、型ガードは覚えておくべき重要な要素です。
7. ユニオン型と配列を組み合わせて表現力を高める
ユニオン型は単体で使うだけでなく、配列と組み合わせることでさらに表現力を高めることができます。例えば、受け取るデータが「値が一つの場合もあれば、複数入った配列になる場合もある」ような場面に役立ちます。
interface Message {
text: string | string[];
}
const m1: Message = { text: "こんにちは" };
const m2: Message = { text: ["おはよう", "こんにちは", "こんばんは"] };
このように、テキストが単体の文字列か複数の文字列の配列かを柔軟に許容することで、メッセージアプリや通知システムなど、さまざまな状況に対応した設計が可能になります。同じ名前のプロパティに複数の入力形式を許可できるため、データの扱いやすさが大きく向上します。
8. ユニオン型を使ったエラーハンドリングの基礎
ユニオン型はエラーハンドリングにも役立ちます。たとえば処理の結果が「成功したときのデータ」または「エラー内容」を返す関数があるとします。こうした場面でユニオン型を使うと、どちらの結果も安全に扱えるようになります。
interface Success {
status: "ok";
data: string;
}
interface Failure {
status: "error";
message: string;
}
type Result = Success | Failure;
function getData(): Result {
if (Math.random() > 0.5) {
return { status: "ok", data: "データ取得成功" };
} else {
return { status: "error", message: "データ取得に失敗しました" };
}
}
const result = getData();
if (result.status === "ok") {
console.log(result.data);
} else {
console.log(result.message);
}
このようにユニオン型を用いることで、成功と失敗の両方のケースを明確に扱えるようになります。型安全性が高まり、エラー時の処理を書き忘れるといった問題も防ぎやすくなります。
まとめ
ここまで、TypeScriptのインターフェースでユニオン型を扱う方法について、基礎から応用までじっくり学んできました。ユニオン型は「複数の型のうちどれか一つを許可する」という柔軟さを備えた仕組みであり、TypeScriptを使って現実に即したデータ構造を表現したいときに大いに役立ちます。たとえば、年齢が数字で入力される場合もあれば「不明」と文字列で扱いたいケースもあり、実務に近いデータ構造をそのまま表現できるのがユニオン型の魅力です。また、支払い方法やステータスのように選択肢が限られている値を安全に扱うときにも適しており、設計の段階で意図しない値が紛れ込まないようにするという重要な役割も果たしてくれます。
インターフェースとユニオン型を組み合わせることで、ただ柔軟なだけでなく、データの安全性や予想しやすさも大きく向上します。特にリテラル型と組み合わせたユニオン型では、選択肢を明確に限定できるため、プログラムを読み返したときに「ここにはどんな値が入るのか」がすぐ分かり、メンテナンスのしやすさにもつながります。さらに、プロジェクトの規模が大きくなるほど、型の設計がそのまま品質に影響するため、インターフェースを使った型管理は避けて通れません。ユニオン型を適切に使い分けることで、複雑なデータを扱う場面でも混乱を防ぎ、堅牢なプログラムを作れるようになります。
ここでは理解を深めるために、記事の内容を応用した追加サンプルも紹介します。複数のユニオン型を用いたインターフェースの例として、ユーザー情報と注文情報を組み合わせたシンプルなプログラムを用意しました。実際にどのようにユニオン型が使われるのかを確認してみましょう。
interface Order {
id: number;
status: "新規" | "処理中" | "完了";
amount: number | string;
}
interface Customer {
name: string;
rank: "一般" | "会員" | "特別会員";
}
const order1: Order = {
id: 101,
status: "新規",
amount: 3000
};
const order2: Order = {
id: 102,
status: "完了",
amount: "未確定"
};
const customer: Customer = {
name: "たろう",
rank: "特別会員"
};
console.log(order1, order2, customer);
このサンプルからも分かるように、ユニオン型は型の柔軟さと安全性を同時に両立できる便利な仕組みです。金額が数値で確定している場合はそのまま扱い、不確定な場合は文字列として保持するなど、実際のアプリケーションに近いデータの扱い方が簡単に表現できます。また、リテラル型を使うことで、注文のステータスやユーザーのランクを誤って入力してしまうのを防げるため、エラーを未然に防ぐ効果が非常に高くなります。TypeScriptを使う上で、ユニオン型は避けて通れない重要な概念であり、理解しておくことでデータ構造の表現力が格段に向上します。
さらに、ユニオン型を活用する際には型ガードの知識も徐々に身につけていくと、より高度な処理にも対応できるようになります。ひとまず初心者の方は、文字列と数値のユニオンや、いくつかの固定された選択肢を表すユニオン型から慣れていくと良いでしょう。TypeScriptの型システムが持つ豊かな表現力を味わいながら、より安全で読みやすいコードへと近づく第一歩になります。
生徒
「ユニオン型って難しそうに思っていたけど、実生活の例と比べるとわかりやすくて驚きました!」
先生
「そうですね。選択肢が複数あって、その中からどれか一つを許可するというイメージを持てれば理解しやすいはずですよ。」
生徒
「インターフェースと組み合わせると、ただの型じゃなくて、データ全体の構造が分かりやすくなるんですね!」
先生
「その通りです。ユニオン型で値を絞り込みつつ、柔軟さも保てるのが大きな魅力です。」
生徒
「今日の内容のおかげで、いろいろなデータを扱う場面でも型をどう使えばいいか、少しイメージが掴めました!」
先生
「その気づきは大きいですよ。これから実際にコードを書きながら慣れていきましょうね。」