Skip to content

oneOf (Union Types)

Soxom generates language-idiomatic union types from oneOf schemas, with full discriminator support for type narrowing.

components:
schemas:
PaymentMethod:
oneOf:
- $ref: '#/components/schemas/CreditCard'
- $ref: '#/components/schemas/BankAccount'
discriminator:
propertyName: type
mapping:
credit_card: '#/components/schemas/CreditCard'
bank_account: '#/components/schemas/BankAccount'
CreditCard:
type: object
required:
- type
- card_number
- expiry_month
- expiry_year
properties:
type:
type: string
enum: [credit_card]
card_number:
type: string
expiry_month:
type: integer
expiry_year:
type: integer
cardholder_name:
type: string
BankAccount:
type: object
required:
- type
- account_number
- routing_number
properties:
type:
type: string
enum: [bank_account]
account_number:
type: string
routing_number:
type: string
account_holder_name:
type: string
// Union type
type PaymentMethod = CreditCard | BankAccount;
interface CreditCard {
type: 'credit_card';
cardNumber: string;
expiryMonth: number;
expiryYear: number;
cardholderName?: string;
}
interface BankAccount {
type: 'bank_account';
accountNumber: string;
routingNumber: string;
accountHolderName?: string;
}

Discriminators tell Soxom (and the SDK) which variant of the union to use.

The most common pattern - a type property indicates the variant:

PaymentMethod:
oneOf:
- $ref: '#/components/schemas/CreditCard'
- $ref: '#/components/schemas/BankAccount'
discriminator:
propertyName: type # The property to check

Map discriminator values to schema names:

PaymentMethod:
oneOf:
- $ref: '#/components/schemas/CreditCard'
- $ref: '#/components/schemas/BankAccount'
discriminator:
propertyName: type
mapping:
cc: '#/components/schemas/CreditCard' # "cc" maps to CreditCard
bank: '#/components/schemas/BankAccount' # "bank" maps to BankAccount

With discriminators, SDKs support type narrowing:

function processPayment(method: PaymentMethod) {
// TypeScript narrows the type based on discriminator
if (method.type === 'credit_card') {
// TypeScript knows this is CreditCard
console.log(`Card ending in ${method.cardNumber.slice(-4)}`);
console.log(`Expires: ${method.expiryMonth}/${method.expiryYear}`);
} else {
// TypeScript knows this is BankAccount
console.log(`Account: ${method.accountNumber}`);
console.log(`Routing: ${method.routingNumber}`);
}
}

If no discriminator is specified, Soxom generates a simple union:

# OpenAPI - no discriminator
Result:
oneOf:
- $ref: '#/components/schemas/Success'
- $ref: '#/components/schemas/Error'
// Generated TypeScript
type Result = Success | Error;
// Manual type checking required
function handleResult(result: Result) {
if ('data' in result) {
// Likely Success
} else if ('error' in result) {
// Likely Error
}
}

Multiple union types in a response:

components:
schemas:
Event:
oneOf:
- $ref: '#/components/schemas/UserCreatedEvent'
- $ref: '#/components/schemas/UserUpdatedEvent'
- $ref: '#/components/schemas/UserDeletedEvent'
discriminator:
propertyName: event_type
mapping:
user.created: '#/components/schemas/UserCreatedEvent'
user.updated: '#/components/schemas/UserUpdatedEvent'
user.deleted: '#/components/schemas/UserDeletedEvent'
UserCreatedEvent:
type: object
properties:
event_type:
type: string
enum: ['user.created']
user:
$ref: '#/components/schemas/User'
timestamp:
type: string
format: date-time
UserUpdatedEvent:
type: object
properties:
event_type:
type: string
enum: ['user.updated']
user:
$ref: '#/components/schemas/User'
changes:
type: object
timestamp:
type: string
format: date-time
UserDeletedEvent:
type: object
properties:
event_type:
type: string
enum: ['user.deleted']
user_id:
type: string
timestamp:
type: string
format: date-time
  1. Always use discriminators for type safety
  2. Use consistent property names like type or kind
  3. Enum the discriminator values in each variant
  4. Document discriminator values in descriptions
  5. Prefer $ref over inline schemas for variants