
Advanced TypeScript Patterns: Branded Types, Discriminated Unions, and Exhaustive Checks
TypeScript's type system is powerful but most devs only scratch the surface. These patterns eliminate entire categories of runtime bugs by making invalid states unrepresentable. 1. Discriminated Unions for State Machines Instead of boolean flags that can conflict: // Bad -- 'loading' and 'data' can both be true interface State { loading : boolean data : User | null error : Error | null } // Good -- only one state at a time type AsyncState < T > = | { status : ' idle ' } | { status : ' loading ' } | { status : ' success ' ; data : T } | { status : ' error ' ; error : Error } // TypeScript narrows automatically function render ( state : AsyncState < User > ) { if ( state . status === ' success ' ) { return state . data . name // TypeScript knows data exists } if ( state . status === ' error ' ) { return state . error . message // TypeScript knows error exists } } 2. Branded Types Prevent mixing semantically different strings/numbers: type UserId = string & { readonly _brand : ' UserId '
Continue reading on Dev.to
Opens in a new tab