Language Foundations

Types

Understand inferred and written types, then choose unions, callable types, or nominal data for the boundary you need.

Types let the checker answer a practical question before a program runs: which values may cross this boundary? Most local values need no written type. Add one when it explains an important input, result, or domain choice.

Start with one checked program

Save this as types.tpz:

TOPAZ
type Stage = "draft" | "ready"

function describe(stage: Stage, checks: int) -> string {
    "{stage} after {checks} checks"
}

let checks = 3
let current: Stage = "ready"
let render: (Stage, int) -> string = describe

print(render(current, checks))

Check the boundaries, then run the program:

BASH
topaz check types.tpz
topaz run types.tpz

The result is:

Output
ready after 3 checks

Read the types from the result

checks has no annotation. Its initializer is an integer, so Topaz infers int. current does have an annotation because the program wants a Stage, not any arbitrary string.

Stage is a transparent alias for the literal union "draft" | "ready". The union says that either listed value is valid and every other string is rejected. The alias gives that choice a useful name, but it does not create a new runtime identity.

render shows a callable type. (Stage, int) -> string means “a function that accepts a Stage and an int, then returns a string.” The named function describe has exactly that shape, so it can be stored in the binding and called later.

The primitive type names are lowercase: int, float, bool, string, and unit (). Common containers use named constructors such as Array<T>, Map<K, V>, and Set<T>. Expected absence and recoverable failure use Option<T> and Result<T, E>.

Choose the shape that states your intent

NeedPreferWhy
An obvious local valueinferenceThe initializer already communicates the type.
A public input, result, or important local boundaryan annotationThe checker and reader see the intended contract directly.
One value from a closed set of alternativesa union or literal-union aliasEach admitted alternative remains explicit.
A value that can be called latera callable type such as (int) -> boolParameter and result types travel with the value.
Domain identity, fields, or named casesa record, enum, or newtypeNominal declarations remain distinct even when their contents look alike.

A type alias improves vocabulary but remains transparent. Choose nominal data when mixing two structurally similar values would be a real mistake. The Records & Nominal Data page compares record, enum, and newtype construction and matching.

Give empty values enough context

Some values do not carry enough information by themselves. An empty array cannot reveal its element type, and None cannot reveal the missing value type. Supply context through an annotation, a function parameter, a declared result, or another surrounding type:

let names: Array<string> = []
let selected: Option<int> = None

These are canonical shapes shown as text here; the runnable example above remains the page’s verified program.

Common mistake: writing the target language’s type spelling

Topaz collection types use Array<T>, Map<K, V>, and Set<T>. Callable types use parentheses around their parameter types, followed by -> and the result type. Keeping these spellings explicit makes checker diagnostics and generated targets agree on the same source contract.

Exact limits

  • There is no implicit conversion between int and float.
  • Generic type parameters are invariant; two different arguments do not become compatible merely because their contents seem related.
  • Generic bounds belong only to named function type parameters. Functions & Generics explains the admitted rank-1 form.
  • Recursive aliases, variance annotations, higher-rank polymorphism, and broader reflection remain deferred.
  • A generic empty value that still has unsolved type parameters is a static error rather than a runtime guess.

If inference, annotations, and mutation are still new, revisit Values and Functions. For choosing among absence, recoverable failure, and faults, continue to Null, Option, Result & Faults.