Designing with states
Standard TypeScript models runtime conditions with untyped exceptions (throw), missing pointers (null/undefined), and raw promises (Promise). In multi-step pipelines, these primitives force defensive branching and obscure edge cases in type signatures.
Synchronous Data Containers
Section titled “Synchronous Data Containers”Absent Values: Maybe<A>
Section titled “Absent Values: Maybe<A>”Accessing missing properties, dictionary keys, or query results yields undefined or null. Handling these with manual if guards interrupts linear pipeline composition.
Maybe<A> represents optionality as a discriminated union: Some<A> (value present) or None (empty). Subsequent mapping steps run only when a value exists:
Typed Synchronous Failures: Result<E, A>
Section titled “Typed Synchronous Failures: Result<E, A>”Functions can throw exceptions of type unknown at any statement without indicating the failure in their return type. Callers must either wrap calls in try/catch or risk unhandled runtime crashes.
Result<E, A> brings failure into the return type: Ok<A> for success or Err<E> for typed errors.
Accumulating Multiple Errors: Validation<E, A>
Section titled “Accumulating Multiple Errors: Validation<E, A>”Result short-circuits on the first failure. While correct for sequential steps, this is counterproductive for form validation and schema parsing where users need a list of all invalid fields at once.
Validation<E, A> evaluates independent checks and accumulates all failures into a non-empty array (NonEmptyArr<E>):
Asynchronous Execution and UI States
Section titled “Asynchronous Execution and UI States”Lazy Infallible Blueprints: Task.Result<E, A>
Section titled “Lazy Infallible Blueprints: Task.Result<E, A>”Standard Promise<T> begins execution immediately upon instantiation and rejects with untyped errors.
Task.Result<E, A> is a zero-argument function that executes only when called and resolves to a Result<E, A>, guaranteeing the outer promise never rejects:
Explicit UI Lifecycle States: RemoteData<E, A>
Section titled “Explicit UI Lifecycle States: RemoteData<E, A>”Modeling async request state with multiple boolean flags (isLoading, error, data) permits invalid combinations, such as displaying a stale error message while a new request is loading.
RemoteData<E, A> defines the four mutually exclusive states of an asynchronous operation:
Concurrency Strategies and Event Pipelines
Section titled “Concurrency Strategies and Event Pipelines”Declarative Async Concurrency: Op<I, E, A>
Section titled “Declarative Async Concurrency: Op<I, E, A>”Managing in-flight request cancellation, debouncing, and retries in UI components typically requires manual AbortController handles and state flags.
Op<I, E, A> separates the async task definition from its execution policy:
Typed Event Sequences: EventBus<S>
Section titled “Typed Event Sequences: EventBus<S>”Standard event emitters leave message payloads untyped, while complex reactive stream frameworks introduce heavy operators for basic event coordination.
EventBus<S> provides a type-safe event pipeline for matching event sequences and accumulating state over time:
Where to go next
Section titled “Where to go next”The choosing guide turns these families into per-situation decisions, Reading the types explains how a container carries its channels, and the recipes build a feature from several modules at once.