Non-Empty Collections — Compile-Time Guarantees
Functions that operate on collections often assume at least one element exists (such as head,
min, max, or bulk database inserts). Because standard arrays (readonly T[]) can be empty,
functions must either return undefined, throw runtime errors, or require defensive length checks.
NonEmpty provides compile-time branded types—Arr.NonEmpty<A>, Str.NonEmpty,
Uniq.NonEmpty<A>, Dict.NonEmpty<K, V>, and Rec.NonEmpty<A, K>—guaranteeing that a collection
or string contains at least one element:
Every access to the head of a potentially empty collection is unsafe. pipelined solves this by
introducing compile-time type-level guarantees that collections, strings, sets, or maps contain at
least one element: Arr.NonEmpty, Rec.NonEmpty, Uniq.NonEmpty, Dict.NonEmpty, and
Str.NonEmpty.
Type Structure
Section titled “Type Structure”Non-empty collections are represented directly using TypeScript structural types:
Arr.NonEmpty<A>: Represented as a structural read-only tuple structurereadonly [A, ...A[]]. Because it extends the standardreadonly A[]interface, it is assignable to standard arrays without conversion.- Branded Non-Empty Types:
Rec.NonEmpty,Uniq.NonEmpty,Dict.NonEmpty, andStr.NonEmptyutilize compile-time phantom brand tags. This guarantees the presence of elements at compile-time while remaining directly assignable to their standard, unbranded counterparts:Uniq.NonEmpty<A>is assignable toReadonlySet<A>Dict.NonEmpty<K, V>is assignable toReadonlyMap<K, V>Rec.NonEmpty<A, K>is assignable toReadonly<Record<K, A>>Str.NonEmptyis assignable tostring
Creating Non-Empty Collections
Section titled “Creating Non-Empty Collections”When receiving collections or strings at the boundaries of your system (such as reading from database queries, parsing API payloads, or processing user inputs), you can refine them using type guards or lift them from known values:
Singletons
Section titled “Singletons”If you already have a value, you can construct a non-empty array, record, set, or map directly:
Refinement (Type Guards)
Section titled “Refinement (Type Guards)”You can refine standard collections or strings using module-level is.nonEmpty type guards. Inside
the conditional blocks, TypeScript automatically narrows the types:
Safe Conversions (Maybe)
Section titled “Safe Conversions (Maybe)”To safely convert potentially empty collections or strings into optional values, use the
module-specific from.* helpers. They return Some if the collection or string contains at least
one item, and None if it is empty:
Modifying and Combining Non-Empty Arrays
Section titled “Modifying and Combining Non-Empty Arrays”Adding elements to standard arrays naturally guarantees a non-empty result. The module helpers
Arr.prepend and Arr.append accept standard, potentially empty arrays and return a guaranteed
Arr.NonEmpty. Once you have a Arr.NonEmpty, you can also concatenate additional items with
Arr.NonEmpty.concat or insert separators between elements with Arr.NonEmpty.intersperse while
preserving the non-empty guarantee:
Transformations and Reductions
Section titled “Transformations and Reductions”Standard library mapping functions operating on standard collections (such as Arr.map or
Rec.map) return standard collections, discarding any compile-time guarantee that the collection is
non-empty.
To transform the elements of a non-empty collection while preserving its non-empty type guarantee,
use the dedicated mapping helpers under the NonEmpty drawer (Arr.NonEmpty.map,
Arr.NonEmpty.mapWithIndex, Rec.NonEmpty.map, Rec.NonEmpty.mapWithKey, Dict.NonEmpty.map,
Dict.NonEmpty.mapWithKey, and Uniq.NonEmpty.map):
When reducing collections down to a single value, use the NonEmpty.reduce helpers. Unlike standard
reductions, these do not require an initial accumulator seed value because the collections are
guaranteed to contain at least one element:
Destructuring and Extracting Elements
Section titled “Destructuring and Extracting Elements”Operating on non-empty collections guarantees safe extraction of members:
- Arrays (
Arr.NonEmpty):headandlastreturn values directly (neverundefined), andtailreturns a standard read-only array of the remaining elements. - Records (
Rec.NonEmpty) and Maps (Dict.NonEmpty): Extractingkeys,values, orentriesreturns a guaranteedArr.NonEmptyarray of those elements. - Sets (
Uniq.NonEmpty): Converting to an array viaUniq.NonEmpty.to.arrayreturns a guaranteedArr.NonEmpty<A>.
Problems it solves
Section titled “Problems it solves”- Enforcing non-empty batch payloads at API boundaries: Endpoints and service methods (such as
bulk database mutations, multi-item checkouts, or batch deletion jobs) require at least one item
to execute validly. Requiring
Arr.NonEmpty<A>orRec.NonEmpty<K, V>guarantees presence at compile time, eliminating defensiveif (arr.length === 0)checks across internal layers. - Guaranteeing error payloads in validation failures: When a validation or multi-rule check
fails, the failure container must contain at least one error message. Returning an empty error
list is an impossible state that confuses UI renderers.
Validationutilizes non-empty collections to ensure failure variants always contain actionable errors. - Navigation breadcrumbs and hierarchical trees: Data models like navigation trails or
organizational hierarchies always possess at least one root node. Modeling breadcrumbs as
Arr.NonEmpty<Crumb>guarantees that extracting the active leaf (Arr.NonEmpty.last) or root (Arr.NonEmpty.head) is always safe and immediate. - Guaranteed non-empty record keys (
Rec.NonEmpty.keys): Extracting keys or values from a verified non-empty dictionary returnsArr.NonEmpty<K>, preserving non-empty guarantees across subsequent transformation stages. - Eliminating mathematically undefined operations on empty sets: Operations like finding
extremes (
Math.min(...[]) === Infinity), reducing collections without fallback values, or computing statistical averages (sum / lengthproducingNaNon empty arrays) are mathematically undefined on empty collections.NonEmptycollections guarantee at least one element exists, makinghead,min,max, and reduction operations structurally safe and non-optional.