Computation as
Instantiation

Type-Level pushes the TypeScript type system past type-checking and into computation. Here the type system behaves as a purely functional language: it runs entire algorithms during compilation, leaving nothing to execute at runtime. Every experiment in the archive is a real program written only in types. Open one, change the input, and the in-browser compiler resolves the answer as you type.

// A whole Wordle game in a single type
type Game = Wordie<10, [
  'starn', 'soman', 'fanos', '', '', ''
]>;
// Game resolves to the finished board

Recursive Induction

There is no runtime step. The type system has no loops and no variables, so every algorithm is written as recursion: a type that refers to itself, peeling one piece off its input on each pass until nothing is left. Three primitives do all the work.

01

String Splitting

A template literal pattern, `${infer Head}${infer Tail}`, splits a string into its first character and the rest: the type-level form of str[0] and str.slice(1).

02

Branching

Conditional types, T extends U ? X : Y, are the only control flow. Each one decides which branch to recurse into, the way an if would at runtime.

03

Tuples as State

Tuples hold the state a type otherwise can't. Spreading into [...P, x] pushes an item; reading P['length'] counts. One tuple can act as a stack, a counter, or a record of values already seen.

Anatomy of a Type

ValidParentheses is the smallest experiment in the archive. Like every type-level function, it is built from three parts, all visible in its first line.

Generics: The parameters. Here, the input string and an accumulator that starts as an empty tuple.
Constraints: extends string fixes what each parameter may hold, so the body can trust its shape.
Conditional: extends ? : picks the next step. The outer one asks whether a character is still left to read.

Reading the Recursion

Here is ValidParentheses in full. It walks the string left to right and uses the tuple P as a stack: every opening bracket is pushed on, and every closing bracket must match whatever sits on top. Once the string is empty, a leftover item on the stack means a bracket never closed.

type ValidParentheses< T extends string, P extends unknown[] = [] > = // any characters left to read? T extends `${infer Head}${infer Tail}` ? Head extends Opener ? ValidParentheses<Tail, [...P, Head]> // push : Match<Pop<P>, Head> extends true ? ValidParentheses<Tail, Popped<P>> // pop : 'INVALID 👎' // string consumed: the stack must be empty : P['length'] extends 0 ? 'VALID 👍' : 'INVALID 👎';

Opener and Match are one-line helper types; the live source spells them out.

“Push the logic into the type system and the runtime cost drops to zero. The compiler has already computed the answer before the program ever runs.”

Browse the experiments →