> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ton.org/llms.txt
> Use this file to discover all available pages before exploring further.

<AgentInstructions>

## Submitting Feedback

If you encounter incorrect, outdated, or confusing documentation on this page, submit feedback:

POST https://docs.ton.org/feedback

```json
{
  "path": "/tolk/types/unions",
  "feedback": "Description of the issue"
}
```

Only submit feedback when you have something specific and actionable to report.

</AgentInstructions>

# Union types

Tolk supports union types such as `T1 | T2 | T3`.

A union type represents a value that can be one of several types. [Pattern matching](/languages/tolk/syntax/pattern-matching) is used to distinguish union variants. A special case `T | null` is written as `T?` and referred to as a [nullable type](/languages/tolk/types/nullable).

```tolk theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
struct (0x12345678) Increment { /* ... */ }
struct (0x23456789) Reset { /* ... */ }

type IncomingMsg = Increment | Reset

fun handle(m: IncomingMsg) {
    match (m) {
        Increment => { /* here m is `Increment` */ }
        Reset =>     { /* here m is `Reset` */     }
    }
}
```

## Arbitrary union types

Union types are not limited to structures. Any types can be combined into a union. The following union types are valid:

* `int | slice`;
* `address | Point | null`;
* `Increment | Reset | coins`;
* `int8 | int16 | int32 | int64`.

Union types are automatically flattened:

```tolk theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
type Int8Or16 = int8 | int16

struct Demo {
    t1: Int8Or16 | int32?   // int8 | int16 | int32 | null
    t2: int | int           // int
}
```

Union types support assignment based on subtype relations. For example, a value of type `B | C` can be passed to or assigned to `A | B | C | D`:

```tolk theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
fun take(v: bits2 | bits4 | bits8 | bits16) {}

fun demo() {
    take(someSlice as bits4);    // ok
    take(anotherV);              // ok for `bits2 | bits16`
}
```

## Exhaustive pattern matching

A `match` expression must cover all possible cases. It must be exhaustive.

```tolk theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
fun errDemo(v: int | slice | Point) {
    match (v) {
        slice => { v.loadAddress() }
        int => { v * 2 }
        // error: missing `Point`
    }
}
```

`match` can be used with nullable types, since `T?` is equivalent to `T | null`. It can also be used as an expression:

```tolk theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
fun replaceNullWith0(maybeInt: int?): int {
    return match (maybeInt) {
        null => 0,
        int => maybeInt,
    }
}
```

## Union type inference errors

Auto-inference of a union type results in an error. If `match` arms produce values of different types, the inferred result is a union, which is typically not intended:

```tolk theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
var a = match (...) {
    ... => beginCell(),
    ... => 123,
};
```

The type of `a` is inferred as `builder | int`. In most cases, this indicates an error in the code. In such cases, the compiler emits the following message:

```ansi theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
error: type of `match` was inferred as `builder | int`; probably, it's not what you expected
assign it to a variable `var a: <type> = match (...) { ... }` manually
```

To resolve this, either explicitly declare `a` as a union type or fix the code if the union is unintended.

The same rule applies to other cases, such as return type inference:

```tolk theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
fun f() {
    if (...) { return someInt64 }
    else { return someInt32 }
}
```

The result is inferred as `int32 | int64`. While it is valid, a single integer type is usually expected. The compiler reports an error. To fix it, explicitly declare the return type:

```tolk theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
fun f(): int {
    if (...) { return someInt64 }
    else { return someInt32 }
}
```

Declaring return types is recommended practice.

## `is` and `!is` operators

Union types can be tested using the `is` operator:

```tolk theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
fun demo(v: A | B) {
    if (v is A) {
        v.aMethod();
    } else {
        v.bMethod();
    }
}
```

## `lazy` matching for unions

In [message-handling](/languages/tolk/features/message-handling), union values are commonly parsed using `lazy`:

```tolk theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
fun onInternalMessage(in: InMessage) {
    val msg = lazy MyUnion.fromSlice(in.body);
    match (msg) {
        // ...
    }
}
```

This pattern is referred to as [`lazy` match](/languages/tolk/features/lazy-loading):

1. No union is allocated on the stack upfront; loading is deferred until needed.
2. `match` operates by inspecting the slice prefix (opcode), instead of checking a type identifier on the stack.

Union types continue to work correctly without `lazy` and follow the same type-system rules.

## Stack layout and serialization

Union types have a complex [stack layout](/languages/tolk/types/overall-tvm-stack), commonly referred to as tagged unions.

[Serialization](/languages/tolk/types/overall-serialization) depends on whether the union consists of structures with manual serialization prefixes:

* if yes; for example, `struct (0x1234) A`, those prefixes are used;
* if no, the compiler automatically generates a prefix tree; for example, `T1 | T2` is called `Either` type: `0` + `T1` or `1` + `T2`.
