Skip to main content
Tolk supports type aliases, similar to TypeScript and Rust. An alias creates a new name for an existing type and remains fully interchangeable with it.
type UserId = int32
type MaybeOwnerHash = bytes32?

fun calcHash(id: UserId): MaybeOwnerHash {
    // ...
}

Aliases are interchangeable with underlying types

UserId and int32 from the above are fully equivalent:
  • id + 1 is okay, will be int
  • someF(id) is okay if someF accepts int32 or int
  • methods for int32 can be called having UserId and vice versa (and for int also, because int32 is assignable to int)
  • a union UserId | int32 makes no sense, it is simply int32
fun demo() {
    var id: UserId = 1;       // ok
    var num: int = id;        // ok
    var h = calcHash(id);
    if (h != null) {
        h as slice;           // bytes32 as slice
    }
}
To obtain a “strict alias”, which defines a distinct type, use a struct with one field:
struct UniqueId {
    value: int32
}
Such a struct has overhead over plain int32, but it becomes a distinct type with its own methods and semantics.

Two equal aliases are considered different

If two aliases share the same underlying type,
type AssetsDict = dict
type BalanceDict = dict
Then they are not assignable to each other. It allows them to have identical methods:
fun AssetsDict.validate(self) { /* ... */ }
fun BalanceDict.validate(self) { /* ... */ }

fun demo(a: AssetsDict, b: BalanceDict) {
    a.validate();      // ok, method 1
    b.validate();      // ok, method 2
    a = b;             // error, can not assign
}
It reminds intN types: int32 is assignable to int and back, int64 also, but assigning int32 to int64 is something strange.

Type aliases are extremely useful with unions

This is a recommended pattern for handling incoming messages: declare a struct for each message, declare a union, parse input into this union, and match over variants:
type IncomingMessage =
    | CounterIncBy
    | CounterReset
    // ...

// ... after parsing a message
match (msg) {
    CounterIncBy => {
        newCounter = curCounter + msg.byValue
    }
    CounterReset => {
        newCounter = 0
    }
    // ...
}
Read about union types.

Type aliases can be generic

type Wrapper<T> = Nothing | Container<T>
Read about generic structs and aliases.

Stack layout and serialization

An alias is identical to its underlying type. Serialization can be overloaded with custom serializers. This is useful in tricky cases where binary encoding cannot be expressed using existing types.
type MyString = slice

fun MyString.packToBuilder(self, mutate b: builder) {
    // custom logic
}

fun MyString.unpackFromSlice(mutate s: slice) {
    // custom logic
}
For details, follow TVM representation and Serialization.