In domain-driven frontend engineering, types are not just annotations for IDE autocomplete; they are mathematical boundaries that guarantee illegal states can never exist at runtime.
The Discipline of the Type System
A well-crafted type system prevents entire classes of software defects before a single line of code is executed in production. In modern TypeScript development, our type declarations act as verifiable contracts between distributed systems, business logic layers, and UI components.
“Avoid ambiguous boolean flags! Model states as explicit Discriminated Unions and leverage nominal branding so the compiler enforces total exhaustiveness across every branch.”
1. Discriminated Unions: Eliminating Ambiguous State
Instead of loose optional fields and multiple boolean flags that allow contradictory states (e.g. isLoading: true and isSuccess: true simultaneously), structure state transitions as tagged unions:
// ❌ Anti-Pattern: Ambiguous and impossible states permittedtype AmbiguousPaymentState = { isLoading: boolean; isSuccess: boolean; isError: boolean; errorMessage?: string; transactionId?: string;};
// ⚡ Clean Architecture: Explicit Discriminated Unionexport type PaymentState = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; transactionId: string; timestamp: Date } | { status: 'failed'; error: Error; retryCount: number };By leveraging TypeScript’s never type, the compiler verifies that every domain state case is handled. If you add a new state in the future, the compiler will refuse to build until all handlers are updated.
import type { PaymentState } from './payment';
export function handlePayment(state: PaymentState): string { switch (state.status) { case 'idle': return 'Ready for customer submission'; case 'loading': return 'Processing transaction with payment gateway...'; case 'success': return `Transaction completed successfully: ${state.transactionId}`; case 'failed': return `Transaction failed: ${state.error.message} (Retry attempt ${state.retryCount})`; default: const _exhaustiveCheck: never = state; return _exhaustiveCheck; }}2. Branded Types for Domain Integrity
Prevent accidental transposition of primitive IDs (such as passing a UserId into a function requiring an OrderId) through nominal type branding:
Nominal Type Branding
Type Branding Definition
type Brand<K, T> = K & { readonly __brand: T };
export type UserId = Brand<string, 'UserId'>;
export type OrderId = Brand<string, 'OrderId'>;Compile-Time Safety
Attempting to assign an OrderId to a variable typed as UserId produces a strict compile error, eliminating parameter transposition bugs in critical domain pipelines.