type-driven-design

Installation
SKILL.md

Type-Driven Design

Core rule: parse, don't validate (Alexis King). Validation inspects a value and returns it unchanged — the knowledge that it passed evaporates immediately, so every downstream function must either re-check or blindly trust. Parsing converts less-structured input into a more-structured type that cannot represent the invalid case, once, at the boundary. From then on, possession of the value is compiler-enforced proof of validity everywhere it travels.

Flag:

  1. Primitive obsession: domain concepts passed as bare string/number. userId: string accepts an email, an id from the wrong system, or "" — all compile. Replace with a branded type whose parse function is the only producer:

    type UserId = string & { readonly __brand: "UserId" };
    function parseUserId(raw: string): UserId { /* check or throw */ }
    

    Branding is required in structurally-typed languages — a bare type UserId = string alias enforces nothing. (Rust/Haskell: newtype; elsewhere: a small nominal wrapper class.) Keep the brand/constructor private so the parse function can't be bypassed.

  2. Comments doing a type's job: "Amount in whole cents; no decimals" on amount: number is a plea. A Cents type whose parser rejects decimals is a law. Any doc comment stating a constraint on a field or parameter is a type waiting to be written — and once the type exists, the comment is deleted, not kept as decoration.

  3. Prose links where the compiler could enforce: a comment like "see REGION_CODES in ../constants" is an unenforced pointer that goes stale silently. Derive instead — type Region = (typeof REGION_CODES)[number] — so the constant is the single source of truth and drift is a compile error, not a doc bug.

Installs
4
Repository
jyecusch/skills
First Seen
13 days ago
type-driven-design — jyecusch/skills