Skip to main content
Tolk supports type conversions: either automatic or explicit via the as operator.

Automatic conversions

Assignable types can be provided explicitly:
fun takeOptInt(v: int?) {}

fun main() {
    takeOptInt(100);    // ok, `int` to `int?`
    takeOptInt(null);   // ok, `null` to `int?`
}
Certain conversions are automatic:
  • int to intN.
  • AliasForT and T (bidirectional).
  • Cell<T> to cell.
fun autocast() {
    var number: int32 = 100;   // auto cast `int` to `int32`
}
Non-assignable types cause a compilation error:
fun main() {
    var number: int = true;
}
/// file.tolk:2:23: error: can not assign `bool` to variable of type `int`
///            hint: use `as` operator for UNSAFE casting: `<some_expr> as int`
///
///     // in function `main`
///    2 |     var number: int = true;
///      |                       ^^^^

Smart casts

Once a variable is checked, the compiler narrows its type. For instance, if a variable could be null before the check, and the condition is confirmed to be true, the variable’s type will not allow null in the true block anymore.
if (lastCell != null) {
    // here lastCell is `cell`, not `cell?`
}

Operator is for union types

Given a union type T1 | T2 | ..., the is operator performs a runtime type test, narrowing the type.
fun demo(v: int | Point | cell) {
    if (v is Point) {
        return v.x + v.y;
    }
    // v is `int | cell` here
}

Non-null assertion with operator !

The ! operator bypasses the compiler’s nullability check. It is similar to ! in TypeScript and !! in Kotlin.
fun doSmth(c: cell) {}

fun analyzeStorage(nCells: int, lastCell: cell?) {
    // Say, according to the logic of this program,
    // having non-zero number of `nCells` must mean
    // that the `lastCell` is not `null`
    if (nCells) {
        // Therefore, one can explicitly instruct
        // the compiler to recognize that fact
        doSmth(lastCell!);
    }
}

Unsafe cast with operator as

When non-trivial type transformations are required, the as operator overrides the restrictions by performing an unsafe type cast. For instance, bool cannot be assigned directly to int. However, a direct cast using the as operator is valid since booleans are represented as TVM integers: true is -1, and false is 0.
var number: int = true as int; // -1
The as operator skips type checking and performs no validation at runtime — it is purely a compile-time type cast.This can lead to sudden errors and hidden bugs. For example, if one cast a slice as address and that slice did not hold a valid address, subsequent program becomes undefined and may error whenever that new “address” is used.
Not all transformations are possible. For example, 42 as cell is invalid. When a cast is inappropriate, the compiler indicates this in diagnostic messages. The as operator converts types that share the same TVM representation:
  • address is a TVM slice, so someAddr as slice is valid.
    • Conversely, someSlice as address is valid.
  • bitsN, where N is between 1 and 1023, is a TVM slice, so someSlice as bitsN is valid.
  • intN, where N is between 1 and 257, is a TVM integer, so someInt64 as int32 is valid.
  • bool is as TVM integer, so someBool as int and someBool as int32 result in -1 or 0.
  • Enums are TVM integers, so someColor as int is valid.
Finally, the as operator cannot be applied to unions: v as Point is incorrect. Use the is operator and smart casts.