Types as a Language

Expert · 20 min read · ▶ live playground · ✦ checkpoint

TypeScript types are more than labels; the type layer is a small language that can compute new types from old ones. Turing-complete means a system can express general computation, and in TypeScript that power shows up as conditional types, recursion, and type parameters before your JavaScript runs.

This is type-level programming, which means writing types that calculate other types instead of values. It sounds huge. Read it like ordinary code: inputs go in, branches choose a path, and a result type comes out.

Branches and type variables

A type parameter is a variable that holds a type. A conditional type is a type-layer branch written T extends U ? X : Y. You already know types are sets, so read extends here as "is this input set assignable, allowed anywhere the target type is expected, to that set?"

This playground builds tiny answers about launch routes. The types run only in the checker. The console.log line is plain JavaScript.

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

→ mina:search

Read RouteName<Path> slowly. "For some type Path, if it matches this route pattern, grab two pieces and return a string pattern. Otherwise return "unknown"." A template literal type is a string pattern type written with backticks, and the infer names are temporary blanks the checker fills when the pattern matches.

Numbers from tuple length

A tuple is an array type with fixed positions. Tuple length arithmetic uses positions as counters: build a tuple with three seats, build a tuple with two seats, spread them together, and read the new length.

That is not how you should add numbers at runtime. It is a taste of how libraries count, split, and preserve tuple positions in function helpers.

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

→ Mina has 5 karma after launch

A recursive type is a type that refers to itself until a stopping case is reached. BuildTuple stops when the tuple length reaches the requested number. Keep this shallow. The goal is to recognize the shape, not to turn the checker into a calculator for your product logic.

Read type-level code like value code

When a type alias looks dense, use the same reading order you use for a function:

  1. Name the inputs.
  2. Find the branch condition.
  3. Find the success output.
  4. Find the fallback output.
  5. Name the final result with a small alias before nesting more work.

That habit makes utility types, reusable type helpers, less mysterious. Extract<T, U> keeps union members assignable to U. ReturnType<T> reads the return value from a function type. Template literal types build string sets. These are the same moves: inputs, branch, output.

Why libraries feel smart

Libraries use type-level programming to keep one source of truth. A route helper can derive params from a path. A database helper can derive row shapes from a table. A public API can derive response types from an endpoint union.

Here is the small version. No server runs here. The playground only models the type relationship.

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

→ Mina launched launch_search on pro

The value-level data stays ordinary. The type-level helper keeps the relationship: choosing "launch.created" gives you the launch response shape, not the karma response shape. That is why type-level programming matters even if you never write a 40-line conditional type yourself. It is the engine behind the hovers you rely on.

Next, you will practice classic type challenges in small pieces. Treat them like reading drills, not a contest to write the shortest possible type.

Checkpoint

Answer all three to mark this lesson complete

+50 XP on completion