meta-design-expressive-typescript
Expressive TypeScript Patterns
Quick Guide: One test decides every change: can a reader follow the flow without simulating any part of it? Where they cannot, the part they would simulate gets a name, and the function becomes an orchestrator — guards, named calls, assembly — over small pure helpers; where they can, the code stays inline. Two compiler checks carry the shape: a
neverdefault fails compilation when a union grows past itsswitch, andas const satisfieschecks a config without widening its literals.
Detailed Resources:
- examples/core.md — orchestrator before/after, named predicates, transforms and constants, guard clauses, exhaustive unions, async orchestrators,
as const satisfies - examples/data-transforms.md — grouping, counting, partitioning and indexing by name; when
pipehelps and when it hurts - reference.md — refactoring moves at a glance, and what each compiler check needs
<critical_requirements>
Before writing expressive TypeScript
Write each orchestrator as guard clauses, then named calls, then the assembly of their results. A reader learns what every step produces from its name; a transformation written inline has to be run in their head before the next line means anything.
Extract what a reader would have to simulate, and leave what reads in one scan. A name is the only thing extraction adds, so a helper whose name restates its single line adds a jump and nothing else.