June 28, 2026
Discriminated Unions(判別可能なユニオン型)
🔗 Reference
- HandBook: Discriminated Unions
- サバイバルTypeScript: 判別可能なユニオン型 (discriminated union)
概要
型の推論を行うための共通のプロパティを定義したユニオン型。
以下は識別子となるtypeが"credit"なのか"bank"なのかでdataの型を分岐させている。
// クレジットカード
type CreditCard = {
code: number;
name: string;
cvc: number;
expiration_month: number;
expiration_year: number;
};
// 銀行口座
type Bank = { account: number; bank_code: number; branch_code: number };
// typeフィールドがDiscriminated Unionsにおける識別子として機能する
type Payment = { type: 'card'; data: CreditCard } | { type: 'bank'; data: Bank };
const handlePayment = (paymentData: Payment) => {
if (paymentData.type === 'card') {
// ここではdataはCreditCard型として推論される
console.log('💳 I am CreditCard type!', paymentData.data);
} else if (paymentData.type === 'bank') {
// ここではdataはBank型として推論される
console.log('🏦 I am Bank type!', paymentData.data);
}
};
判別のための識別子は複数のフィールドで構成することも可能。
以下はtypeで大分類、subtypeで中分類を用意して複合的に型を分岐させている例。
type MultiDiscriminatedUnion =
| { type: 'credit'; subtype: 'master'; data: string }
| { type: 'credit'; subtype: 'visa'; data: number }
| { type: 'bank'; subtype: 'yu_cho'; data: boolean }
| { type: 'bank'; subtype: 'other'; data: string[] };
const Test = (option: MultiDiscriminatedUnion) => {
if (option.type === 'credit') {
if (option.subtype === 'master') {
// data は string型として判断
console.log('I am MasterCard!', option.data);
} else if (option.subtype === 'visa') {
// data は number型として判断
console.log('I am VisaCard!', option.data);
}
} else if (option.type === 'bank' && option.subtype === 'yu_cho') {
// data は boolean型として判断
console.log('I am ゆうちょ銀行!', option.data);
}
};
識別子として使える型
- 文字列リテラル型(
"success"、"error"など) - 数値リテラル型(
1,100など) - 論理値リテラル型(
trueorfalse) nullundefined
type DiscriminatedUnion =
| { type: '文字列'; data: string }
| { type: 1; data: number }
| { type: true; data: boolean }
| { type: null; data: string[] }
| { type: undefined; data: number[] };
const Test = (option: DiscriminatedUnion) => {
if (option.type === '文字列') {
console.log(option.data); // data: string
} else if (option.type === 1) {
console.log(option.data); // data: number
} else if (option.type === true) {
console.log(option.data); // data: boolean
} else if (option.type === null) {
console.log(option.data); // data: string[]
} else if (option.type === undefined) {
console.log(option.data); // data: number[]
}
};
ZodでのDiscriminated Unions
Discriminated unions | zod.dev
TypeScriptのDiscriminated Unionsと同じ感覚でフォームの入力状態に合わせてスキーマを定義できる。
const Payment = z.discriminatedUnion('payment_type', [
z.object({
payment_type: z.literal('credit'),
code: z.string(),
// ....
}),
z.object({
payment_type: z.literal('bank'),
bank_code: z.string(),
// ...
}),
]);
name="payment_type"なラジオボタンのvalueを定義して、そのvalueがcreditなのかbankなのかでスキーマを判別できるようにするみたいなことが可能。
zod-form-validateを活用すれば決済方法に応じて適切にバリデーションを行うこともできる。