Tuple types
A tuple type describes a fixed-length sequence where each position has its own type, both length and per-position types being part of the type. You read an element with a literal index and get that position's exact type; an out-of-bounds or non-literal index is a compile error. Assignability is invariant and length-exact.
A tuple type describes a fixed-length sequence where each position has its own type. Both the length and the per-position types are part of the type.
type Pair = [string, number]
A Pair is exactly two elements: a string followed by a number. This is how you model a small, fixed record addressed by position rather than by name — a coordinate, a key-value entry, the paired result of a function that returns two things.
Positional access
You read an element with a constant index, and you get back that position's exact type. The index must be a static, non-negative integer literal.
const p: [string, number] = ["a", 1]
p[0] // string
p[1] // number
An index past the end of the tuple is a compile-time error, not a value typed T | undefined. Because the length is known, the checker can tell that the access can never succeed and refuses it outright.
const t: [string, number] = ["a", 1]
t[2] // error: index out of bounds
An index that is not a literal — a variable, a computed value — is refused as well. The checker only allows an access it can resolve to a specific position at compile time.
What you can assign
Tuple assignability is invariant and length-exact. Two tuple types match only when they have the same length and each position is equivalent. There is no length flexibility and no positional covariance, and a tuple is never interchangeable with an array of the same element type. A [string, number] is not an Array<string | number> and vice versa.
const bad: [string, number] = ["a", 1, true] // error: element-count mismatch
This is stricter than TypeScript, which allows a range of tuple-to-tuple and tuple-to-array conversions. CruftScript treats a tuple as an exact shape.
Why it works this way
The value of a tuple is that its length and layout are known facts the checker can rely on. Making out-of-bounds access a compile error, rather than widening it to T | undefined, turns a whole class of off-by-one mistakes into errors you see before the program runs. Requiring a literal index is what makes that possible: the checker can only prove an access is in bounds when it knows which position you mean. Invariant equivalence keeps those guarantees intact, since any looser rule would let a tuple be viewed through a type that misstates its length or its positions.
Limitations
- Only the plain fixed form is supported. Optional elements, rest elements, named elements, and spread elements are not in the accepted grammar. A malformed interior segment is silently dropped rather than reported, so avoid writing these forms at all — the result will not be what you wrote.
readonlyis erased. Areadonly [string, number]parses, but the modifier is discarded and has no effect on the resulting type.- No dynamic indexing. You cannot index a tuple with a variable. Every access needs a literal position known at compile time.