CruftScript type system

The overview and directory of CruftScript's static type system: the shared rules every construct obeys (no any, unknown must be checked before use, an undecidable answer is an error) and links to one page per type construct, from primitives through generics and conditional types.

CruftScript has a static type system: you annotate your code with types, and the compiler checks them before the program runs. The shape of it will be familiar if you have used TypeScript, because the syntax is close on purpose. What is different is the stance: where TypeScript will, in a few well-known places, take your word for something it cannot prove, CruftScript refuses. A type in CruftScript is a promise the compiler actually keeps.

This section is one page per construct. Start here for the ideas that run through all of them, then follow the directory below to the piece you need.

The rules they all obey

A handful of principles are shared by every construct, so they are worth stating once up front.

  • There is no any. TypeScript's any is a value the checker stops reasoning about. CruftScript has no such keyword. unknown is the only catch-all, and it must be checked before use.
  • unknown is a value you have to check. Anything can be stored as unknown, but you cannot use it until a real runtime check tells you what it is. There is no cast or assertion that skips that check.
  • When the compiler cannot decide, it stops. If a type computation has no provable answer, the compiler reports an error instead of guessing. This is the most common way CruftScript differs from TypeScript, which tends to fall back to a broad type and continue.
  • The defaults are the safe ones. Container types are compared strictly, recursive type computations must be shown to finish, and the unsafe escape hatches TypeScript offers (!, as unknown as T) are either removed or turned into checks that actually run.

A practical note: the type checker is broad and works today, but the subset of CruftScript that runs directly is narrow, so most programs that use these annotations run through the ordinary compile-and-strip path. Each page says plainly where a construct is fully working and where it is designed but not yet accepted.

The types

Foundations

  • Primitive typesstring, number, boolean, and the rest, compared by name.
  • The unknown type — the one catch-all, and why you must check it before use.
  • The never type — the empty type, and how it signals an impossible or fully-handled case.

Values as types

Composite types

  • Object types — structural records, matched by shape.
  • Tuple types — fixed-length, position-typed sequences.
  • Array typesT[] and Array<T>, checked element by element.

Combining types

Narrowing

  • Type narrowing — the runtime checks that refine a value's type in a branch.
  • Type predicates — your own narrowing functions, and why their bodies are not taken on faith.

Generics

Type-level computation

Inference and assertions

Runtime-bearing constructs