Library names note: Helper and member calls beyond the standard-library minimum (
fileExists,readFileContent,fetch,.ok,.json,.status,toUpperCase,Date, etc.) are illustrative placeholders, not canonical APIs.
Topaz has a static type system with type inference, so code stays safe without getting verbose. The compiler infers types automatically when you don't specify them, but you can be explicit when needed.
Primitive Types
Integers and Floats
// Integer
let age: int = 25
let bigCount: int = 9999999999
// Float
let price: float = 19.99
let pi: float = 3.141592653589793
// Type inference
let score = 95 // inferred int
let average = 87.5 // inferred floatTopaz primitives are
int(64-bit signed),float(IEEE-754 binary64),string,bool, and(). The()is also an expression: the sole value of the unit type. Arbitrary-precision integers are not specified in Topaz v5.2, andint/floatnever mix implicitly in arithmetic.
Boolean
let isActive = true
let hasAdminRights = false
// Used in conditionals
if isActive {
print("Service is active")
}Strings
// String (single character or longer)
let grade: string = "A"
let emoji: string = "😀"
let name = "John Topaz"
let longSentence = "Welcome to Topaz — one way to say it!"
// String interpolation
let greeting = "Hello, {name}! Nice weather today."
print(greeting) // "Hello, John Topaz! Nice weather today."Single-quoted string and character literals (
'A','') are not canonical Topaz syntax. Use double-quoted strings for both single characters and longer text. Strings are not indexable and expose no.length; scalar-level access goes throughs.scalars().
Collection Types
Arrays
// Array declaration and initialization
let mut numbers: Array<int> = [1, 2, 3, 4, 5]
let names = ["Alice", "Bob", "Charlie"] // inferred as Array<string>
// Array manipulation
let first = numbers[0] // 1 — direct indexing faults when out of bounds
let safe = numbers.get(0) // Some(1) — non-faulting read returns Option<int>
numbers.push(6) // [1, 2, 3, 4, 5, 6]
let length = numbers.length // 6
// Functional style operations
let squares = map(numbers, x => x * x)
let evens = filter(numbers, x => x % 2 == 0)Records
// Record definition
let user = {
name: "John Topaz",
age: 28,
email: "john@example.com",
isActive: true
}
// Property access
print(user.name) // "John Topaz"
// Immutable update with record literals
let calculator = { value: 0 }
let afterAdd = calculator{ value: calculator.value + 10 }
let afterMultiply = afterAdd{ value: afterAdd.value * 2 }
print("{afterMultiply.value}") // 20
print("{calculator.value}") // 0 (original unchanged)
// Reusable behavior can be written as regular functions
function addToCalculator(state: { value: int }, x: int) -> { value: int } {
state{ value: state.value + x }
}
function multiplyCalculator(state: { value: int }, x: int) -> { value: int } {
state{ value: state.value * x }
}Advanced Types
Option Type
// Option type for null safety
let searchResult: Option<string> = None
let username: Option<string> = Some("admin")
// Safe handling with pattern matching
match username {
case Some(name) => print("Welcome, {name}!")
case None => print("Login required")
}
// Safe handling with chaining (strings expose no .length;
// scalar count goes through s.scalars())
let nameLength = username?.scalars()?.length ?? 0
let display = username ?? "guest"Result Type
// Result type for error handling
function readFile(path: string) -> Result<string, string> {
if fileExists(path) {
Ok(readFileContent(path))
} else {
Err("File not found")
}
}
// Safe error handling
match readFile("data.txt") {
case Ok(content) => print("File content: {content}")
case Err(errorMessage) => print("Error occurred: {errorMessage}")
}Union Types
// Type that can be one of several types
type ID = int | string
type Status = "pending" | "processing" | "completed" | "failed"
let userID: ID = 12345
let taskStatus: Status = "processing"
// Type-specific handling with pattern matching
function displayID(id: ID) -> string {
match id {
case numeric: int => "Numeric ID: {numeric}"
case text: string => "String ID: {text}"
}
}Generic Type Use
let numbers: Array<int> = [1, 2, 3]
let maybeName: Option<string> = Some("Alice")
let parsed: Result<User, string> = parseUser(rawJson)
let cache: Map<string, int> = Map.new()Generic Functions and Aliases
Topaz supports rank-1 generic function declarations and generic type
aliases. Type parameters are inferred at each call site; explicit
call-site type arguments such as f<int>(x) are not part of Topaz v5.2.
Pin a type with an annotated binding, parameter, or return position
instead.
function head<T>(xs: Array<T>) -> Option<T> {
match xs {
case [] => None
case [first, ..] => Some(first)
}
}
let first: Option<int> = head([1, 2, 3])
type Pair<T> = { first: T, second: T }
let bounds: Pair<int> = { first: 0, second: 100 }Generic bounds, constraints, and interfaces remain deferred.
Type Aliases
// Give simple names to complex types
type UserInfo = {
name: string,
age: int,
email: string,
permissions: Array<string>
}
type CallbackFunction = (string) -> ()
// Usage example
let admin: UserInfo = {
name: "Admin User",
age: 35,
email: "admin@topaz.ooo",
permissions: ["read", "write", "delete"]
}Type Conversion
let stringNumber = "123"
// toInt returns Option<int> — handle the None case explicitly
let parsed = match toInt(stringNumber) {
case Some(value) => value
case None => 0
}The Topaz stdlib defines
toInt(text: string) -> Option<int>as the canonical conversion helper, which never fails silently. Other conversion forms shown in legacy examples (float(...),string(...),tryInt(...)) are illustrative placeholders, not canonical APIs.
Advanced Patterns
Destructuring Assignment
// Array destructuring
let [first, second, ..rest] = [1, 2, 3, 4, 5]
print("{first}") // 1
print("{rest}") // [3, 4, 5]
// Object destructuring
let { name, age } = user
print("Name: {name}, Age: {age}")
// Destructure inside the function body
function greet(user: { name: string, age: int }) {
let { name, age } = user
print("Hello, {name}! You are {age} years old.")
}Type Guards
Topaz narrows types via
match+ TypePattern, not viais/as/any.anytype andis/asoperators are not canonical Topaz syntax. See the control-flow page for match-based narrowing examples.
Real-world Usage
// Practical type definitions for real projects
type User = {
id: int,
name: string,
email: string,
createdAt: string
}
type UserResponse = {
success: bool,
data: Option<User>,
error: Option<string>,
statusCode: int
}
// User-fetching API
function getUser(id: int) -> UserResponse {
let response = fetch("/api/users/{id}")
if response.ok {
let userData = response.json()
return {
success: true,
data: Some(userData),
error: None,
statusCode: response.status
}
} else {
return {
success: false,
data: None,
error: Some("User not found"),
statusCode: response.status
}
}
}The type surface is small and predictable. Type inference keeps everyday code concise, and you can add precise annotations where you want the compiler to catch mistakes.