Skip to content

anyOf Handling

Soxom interprets anyOf as oneOf to avoid type explosion and provide predictable SDK types.

In OpenAPI, anyOf means “match one or more of these schemas.” This creates challenges for type generation:

# OpenAPI anyOf
Value:
anyOf:
- type: string
- type: integer
- type: array
items:
type: string

Theoretical valid values:

  • "hello" - string only
  • 42 - integer only
  • ["a", "b"] - array only
  • But also… combinations? The spec is ambiguous.

Type explosion problem:

// What type should we generate?
type Value =
| string
| number
| string[]
| (string & number) // Impossible
| (string & string[]) // Impossible
| (number & string[]) // Impossible
| (string & number & string[]) // Impossible

This provides:

  • Predictable types
  • Clean SDK code
  • Better developer experience
  • Consistent behavior across languages
# OpenAPI
Result:
anyOf:
- $ref: '#/components/schemas/Success'
- $ref: '#/components/schemas/Error'

Soxom generates (same as oneOf):

// TypeScript
type Result = Success | Error;
# Python
Result = Success | Error

In practice, APIs using anyOf almost always mean oneOf:

  • Response is either a success OR an error
  • Value is either a string OR a number
  • Data is either format A OR format B

True “any combination” semantics are rare and usually indicate spec issues.

Treating anyOf as oneOf ensures:

  • Type-safe code generation
  • Clear discriminator handling
  • Consistent serialization/deserialization
  • Better IDE autocomplete

This approach aligns with other SDK generators like Speakeasy, providing familiar behavior for API developers.

When you mean “one of these options”, use oneOf:

# Recommended
PaymentMethod:
oneOf:
- $ref: '#/components/schemas/CreditCard'
- $ref: '#/components/schemas/BankAccount'
discriminator:
propertyName: type

When you need combined properties, use allOf:

# For combining schemas
Employee:
allOf:
- $ref: '#/components/schemas/Person'
- $ref: '#/components/schemas/Employment'

If you have anyOf in your spec, consider:

  1. Convert to oneOf if values are mutually exclusive
  2. Convert to allOf if values should be combined
  3. Restructure if neither fits

Before:

Response:
anyOf:
- $ref: '#/components/schemas/SuccessResponse'
- $ref: '#/components/schemas/ErrorResponse'

After:

Response:
oneOf:
- $ref: '#/components/schemas/SuccessResponse'
- $ref: '#/components/schemas/ErrorResponse'
discriminator:
propertyName: status
mapping:
success: '#/components/schemas/SuccessResponse'
error: '#/components/schemas/ErrorResponse'

Before:

# Trying to express "string or number"
FlexibleId:
anyOf:
- type: string
- type: integer

After:

# Just use string - IDs should be strings anyway
FlexibleId:
type: string
description: ID can be numeric but is always transmitted as string
# Often seen for nullable types
MaybeUser:
anyOf:
- $ref: '#/components/schemas/User'
- type: 'null'

Soxom handles this as a nullable type:

type MaybeUser = User | null;
# Sometimes used for inheritance
ExtendedUser:
anyOf:
- $ref: '#/components/schemas/User'
properties:
extra_field:
type: string

Soxom treats this as simple extension (like allOf).

KeywordUse WhenSoxom Behavior
oneOfMutually exclusive optionsUnion type with discriminator
allOfCombining/inheriting schemasComposition/inheritance
anyOfAvoid if possibleTreated as oneOf