Type Annotations
Optional, gradually-checked TypeScript-style type annotations in Tish.
Tish supports optional TypeScript-style type annotations. They're gradual: omit them and a binding is dynamic; add them and you get an opt-in compile-time checker plus faster native code. On their own, annotations never change runtime behavior.
Syntax
// Variable declarations
let x: number = 42
const name: string = "hello"
let arr: number[] = [1, 2, 3]
// Function parameters and return types
fn add(a: number, b: number): number {
return a + b
}
// Object types
let person: { name: string, age: number } = { name: "Alice", age: 30 }
// Union and optional (T? === T | null)
let value: number | string = 42
let maybe: number? = null
// Rest parameters
fn sum(...args: number[]): number { ... }Supported Types
// Generics, interfaces, tuples, literals, intersections, casts
fn identity<T>(x: T): T { return x }
type Box<T> = { value: T }
interface Point { x: number, y: number }
interface Dog extends Animal { bark: boolean }
type Person = Named & Aged
type Status = "on" | "off"
let pair: [number, string] = [7, "z"]
let n = (42 as number) + 8Type checking (--check)
A gradual checker verifies annotations at build time. It is opt-in and conservative — it reports only mismatches it can prove (zero false positives), so unannotated code stays fully dynamic.
tish build app.tish --check warn # report diagnostics, keep building
tish build app.tish --check error # report AND fail the build (exit 1)tish type warning: 2:17: Type 'number' is not assignable to type 'string'.
Set TISH_CHECK=warn (or error) to make it the default for a project, and gate CI on --check error. The checker resolves type aliases, arrays, and object/interface shapes; it does not yet narrow literal-union membership or perform full TypeScript inference.
Native codegen
With --native-backend rust, annotations lower to native Rust types — number → f64, string → String, boolean → bool, number[] → Vec<f64>, and Box<number> monomorphizes to a struct with an f64 field — with no Value boxing. An inference pass also propagates these types through arithmetic, loop counters, and homogeneous arrays, so much idiomatic numeric code goes native without any explicit annotations. See Native backend.
Notes
- Annotations are optional; omitting them is equivalent to dynamic typing.
- They do not change evaluation by themselves — enforcement is the opt-in
--checkpass, not a runtime cost. - Generic type parameters are erased for checking (gradual) but specialized in native codegen.