You've typed arr.length a thousand times at runtime. At the type level it's the first puzzle every utility library opens with. Hand the compiler 123 and ask: how long is it?
Seven puzzles sit dark on the workbench. This is the first — the one that teaches your code HOW LONG a tuple is, without running any of it. The job sounds trivial: “`[1, 2, 3]` has three elements.” You know it; the compiler knows it; somewhere between your brain and your codebase the knowledge gets lost. Your form library wants form[0].name to be the literal "email", not the widened string. Your router wants '/users/$id' parsed into { id: string }, not Record<string, unknown>. Every one of those pipelines starts with the same primitive: reading a single VALUE off a tuple TYPE. Length is the warm-up — four lines of prose, one line of code, one panel green. Six puzzles after it.
// Attempt 1 — the stale definition. It compiles.// It also throws away every literal the caller knew.type Length<T extends readonly unknown[]> = numbertype L = Length<[1, 2, 3]>// ^? number ← the shape is right. The TRUTH is gone.You reach for T.length. That's value-level — it errors. typeof T.length — also value-level, also errors. You're writing a type, not running code.
The workbench above has four panels. The top is a recipe editor — it'll rewrite itself, one line at a time, as you work through three checkpoints. Below that, an evaluator shows what the CURRENT recipe produces when handed 123 — the canonical three-element tuple. Then the four-fixture test suite that'll go from pending to pass as the recipe lands. Then three beads showing which checkpoints you've cleared. Read the recipe pane. It's the naive first attempt — a one-line alias that compiles cleanly and achieves NOTHING. The answer it gives is technically right and practically useless.
Read the recipe carefully. type Length<T extends readonly unknown[]> = number takes a tuple on the left and returns the constant number on the right. If we apply it to 123, what type does the compiler report for L?