Skip to main content
Union types allow a value to be one of several specified types. This is particularly useful for function calling, where different return types may be needed depending on context.

Syntax

Type1 | Type2
union
A type that can be any of the specified types
int | string              // Can be an integer or string
(int | string) | MyClass  // Parentheses for clarity
string | MyClass | int[]  // Multiple types including arrays
Order is important! int | string is NOT the same as string | int.For example, "1" will be parsed as:
  • int when the type is int | string
  • string when the type is string | int
The parser attempts to match types from left to right.

Basic Union Example

function ProcessData(input: int | string) -> string {
    client "openai/gpt-4o-mini"
    prompt #"
        Process this input: {{ input }}
        {% if input is number %}
        (Treating as a number)
        {% else %}
        (Treating as text)
        {% endif %}
    "#
}

test NumericInput {
    functions [ProcessData]
    args { input 42 }
}

test TextInput {
    functions [ProcessData]
    args { input "hello" }
}

Generated Types

from typing import Union

input: Union[int, str] = 42
# or
input: Union[int, str] = "hello"

Literal Union Types

Added in BAML v0.61.0
Primitive types can be constrained to specific literal values:
function ClassifyIssue(issue_description: string) -> "bug" | "enhancement" {
    client "openai/gpt-4o-mini"
    prompt #"
        Classify the issue based on the following description:
        {{ ctx.output_format }}

        {{ _.role("user")}}
        {{ issue_description }}
    "#
}

test BugReport {
    functions [ClassifyIssue]
    args { issue_description "App crashes on startup" }
}

Generated Types

from typing import Literal

result: Literal["bug", "enhancement"] = "bug"

Union with Classes

Unions are powerful for function calling scenarios:
class EmailAction {
    recipient string
    subject string
    body string
}

class CalendarAction {
    event_name string
    date string
    duration_minutes int
}

class SearchAction {
    query string
    max_results int
}

function DetermineAction(user_request: string) -> EmailAction | CalendarAction | SearchAction {
    client "openai/gpt-4o-mini"
    prompt #"
        Based on the user's request, determine which action to take:
        {{ ctx.output_format }}

        User request: {{ user_request }}
    "#
}

test EmailRequest {
    functions [DetermineAction]
    args { user_request "Send an email to [email protected] about the meeting" }
}

test CalendarRequest {
    functions [DetermineAction]
    args { user_request "Schedule a 30-minute meeting tomorrow at 2pm" }
}

Generated Types

from typing import Union
from baml_client.types import EmailAction, CalendarAction, SearchAction

result: Union[EmailAction, CalendarAction, SearchAction] = EmailAction(
    recipient="[email protected]",
    subject="Meeting",
    body="..."
)

Union with Optional Types

function GetValue(key: string) -> int? | string {
    client "openai/gpt-4o-mini"
    prompt #"
        Return the value for key: {{ key }}
        If it's a number, return the number.
        If it's text, return the text.
        If not found, return null.
    "#
}

Generated Types

from typing import Union, Optional

result: Union[Optional[int], str] = None  # null case
# or
result: Union[Optional[int], str] = 42    # int case
# or
result: Union[Optional[int], str] = "text" # string case

Union Arrays

Combining unions with arrays:
function ProcessMixedList(data: (int | string)[]) -> string {
    client "openai/gpt-4o-mini"
    prompt #"
        Process this list of mixed data: {{ data }}
    "#
}

test MixedData {
    functions [ProcessMixedList]
    args { data [1, "hello", 42, "world"] }
}

Generated Types

from typing import List, Union

data: List[Union[int, str]] = [1, "hello", 42, "world"]

Complex Union Examples

Example 1: Optional Int, String Array, or Class

int? | string[] | MyClass

Example 2: Array of Union Optional Types

(int? | string[] | MyClass)[]

Example 3: Literal Values Union

"str" | 1 | false

Using Unions in Prompts

BAML automatically formats union types in prompts using ctx.output_format:
function ExtractData(text: string) -> int | string | bool {
    client "openai/gpt-4o-mini"
    prompt #"
        Extract structured data from the text.
        {{ ctx.output_format }}

        Text: {{ text }}
    "#
}
The ctx.output_format will generate appropriate JSON schema describing the union type for the LLM.

Type Checking at Runtime

When working with union types in your code:
from baml_client import b
from baml_client.types import EmailAction, CalendarAction

result = await b.DetermineAction("Send email to [email protected]")

if isinstance(result, EmailAction):
    print(f"Email to: {result.recipient}")
elif isinstance(result, CalendarAction):
    print(f"Calendar event: {result.event_name}")
else:
    print(f"Search query: {result.query}")

Best Practices

  1. Order matters - Place more specific types before general ones
  2. Use literal unions for fixed sets of string/int/bool values instead of enums when you don’t need descriptions
  3. Document unions - Add comments explaining what each type variant represents
  4. Type narrowing - Use appropriate type checking in your client code
  5. Consider enums - For classification with descriptions, use enums instead of literal string unions

Validation Rules

Parsing Priority:BAML attempts to parse union types from left to right. The first successful parse wins.
// "123" will parse as int
function Example1() -> int | string { ... }

// "123" will parse as string  
function Example2() -> string | int { ... }

Build docs developers (and LLMs) love