TypeScript Patterns We Use in Every Production App
After shipping dozens of apps, these are the TypeScript patterns that actually matter — the ones that prevent bugs, improve readability, and scale cleanly.
TypeScript adds real value when used correctly. It adds noise and false confidence when used poorly. Here are the patterns we've settled on after shipping production apps for clients across industries.
Discriminated Unions for State Management
Instead of boolean flags that can conflict, use discriminated unions to model state explicitly.
```typescript
type RequestState
This makes impossible states impossible. You can't have both `data` and `error` defined. TypeScript will narrow correctly in every branch.
Branded Types for IDs
Raw string IDs are a footgun. Pass the wrong ID to the wrong function and TypeScript won't catch it.
```typescript type UserId = string & { readonly __brand: 'UserId' }; type OrderId = string & { readonly __brand: 'OrderId' }; ```
Now passing an `OrderId` where a `UserId` is expected is a compile error, not a runtime bug.
Zod for Runtime Validation at Boundaries
TypeScript types disappear at runtime. Any data crossing a system boundary — API responses, form inputs, environment variables — needs runtime validation.
We use Zod for this. Define the schema once, infer the TypeScript type from it, and validate at the boundary. Zero duplication.
```typescript const LeadSchema = z.object({ name: z.string().min(1), email: z.string().email(), service: z.enum(['web', 'seo', 'ai', 'mobile']), });
type Lead = z.infer
Strict Null Checks and No Implicit Any
These should be enabled on every project from day one. Retrofitting a codebase that didn't have them is painful. Starting without them means TypeScript isn't actually protecting you.
Result Types Instead of Throwing
Functions that can fail should return a result type, not throw. Thrown errors are invisible in the type system.
```typescript
type Result
Call sites now have to handle the error case. It's enforced by the compiler, not by code review.
The Pattern That Matters Most
Strict types at boundaries, flexible internals. Don't fight TypeScript in your core business logic — let it catch your mistakes. But don't let type gymnastics make simple code unreadable. Know when to use `as` and when to fix the types properly.
More from the Blog
The Complete Next.js Performance Guide for 2026
Core Web Vitals, image optimization, caching strategies, and everything else you need to ship a Next.js app that scores green across the board.
Why Most Agency Websites Fail at SEO (And How to Fix Them)
Most agency websites are beautiful and technically broken. Here's the technical SEO checklist we run on every site we build.
React Native vs Native Development in 2026: The Honest Answer
We've shipped over 30 apps using both approaches. Here's when React Native wins, when native wins, and why the answer is usually React Native.
