Variables

Variables

let introduces a binding. There are three forms.

let count: number = 0     // explicit type with a value
let count = 0             // type inferred from the value
let count: number         // typed, no value yet

Reassignment uses the name alone:

count = count + 1

Inference

let x = value takes the type from the initializer, so the annotation is optional wherever the value already says what the type is:

let names = new Array<string>()
let total = 0
let ready = names.size() > 0

Annotating anyway is still useful when the initializer does not read clearly, or when you want the declaration to state intent rather than repeat a call.

A typed declaration may omit the initializer, which declares the name and leaves it uninitialized until an assignment reaches it. The analyzer tracks that and reports a read before the first assignment.

Globals always need a type

Inference is a local-scope feature. A declaration at module scope has to state its type:

let WORKSPACE_ROOT: string = resolve_workspace_root()

Leaving the type off a global is an error: global variable declarations must include an explicit type, because inference is only available for locals.

Destructuring

let (a, b) = pair binds each name to a positional element of the value. The first name takes get_first, the second get_second, and the element types are inferred from those accessors:

let (key, value) = entry

The same pattern works in a for loop. See the loops page.

Fields

Class fields are declared without let, inside the class body:

class Counter {
    count: i64
    label: string
}

Fields carry no let and no inference: a field always states its type.

Scope

A binding is visible from its declaration to the end of the enclosing block. Blocks nest, and an inner block can shadow a name from an outer one.