"After enough TypeScript, you start designing in types before you write the code. That's not a bug; it's the upgrade."
The shift
Most programmers come to TypeScript from JavaScript and treat types as a layer of bookkeeping on top of the 'real' code. Annotations are a tax; inference is a relief. The mindset is 'I'm writing JavaScript that happens to type-check.'
After enough TypeScript, the mindset flips. Types become the design surface. You sketch the shape of the data and the relationships between functions in types first — then the implementation falls into place because the contracts are already drawn.
What that looks like in practice
Designing a new feature, you ask:
- What's the shape of the data this works on? (A type.)
- What are the legal states this thing can be in? (A discriminated union.)
- What's the public API surface? (A set of function signatures.)
- What invariants must hold? (Where do narrowing checks need to live?)
The answers are types. Once they're typed, the implementation is mostly mechanical. The hard thinking is done at the type level.
OOP universe and type-shaped thought
Dad's 'OOP as universe operating principle' applies here. The principles you'd use to model the world with classes — inheritance, polymorphism, encapsulation, abstraction — appear in types: extension via intersection, polymorphism via generics, encapsulation via private/#private, abstraction via interfaces. TypeScript isn't competing with OOP; it's encoding the same principles into the type layer.