compose
Creates a right‑to‑left function composition pipeline. Each function receives the output of the next function in the chain. Supports both synchronous and asynchronous functions and guarantees that it never mutates input and never throws. If a function throws synchronously, the pipeline returns a rejected Promise instead of propagating the throw.
Use compose when you want a predictable, intention‑revealing transformation chain that reads from right to left.
Signature
function compose<T>(value: T): T;
function compose<T, A>(value: T, fn1: (v: T) => A | Promise<A>): A | Promise<A>;
function compose<T, A, B>(
value: T,
fn1: (v: T) => A | Promise<A>,
fn2: (v: A) => B | Promise<B>
): B | Promise<B>;
// Additional overloads for longer pipelines…
Parameters
Returns
One of:
T— if no functions are provided.- The final transformed value — if all functions are synchronous and none throw.
- A
Promiseresolving to the final value — if any function is asynchronous. - A
Promiserejecting with an error — if any function throws synchronously or returns a rejected Promise.
Behavior
- Pure function: no side effects.
- Never mutates input.
- Never throws.
- Synchronous throws inside pipeline functions are converted into rejected Promises.
- Applies functions right‑to‑left.
- If no functions are provided, returns the initial value unchanged.
- If any function returns a Promise, the entire pipeline becomes asynchronous.
- If a function throws, the pipeline becomes asynchronous and returns a rejected Promise.
Examples
compose(2, n => n + 1, n => n * 2)
// inc(double(2)) = inc(4) = 5
await compose(3, n => n + 1, async n => n * 2)
// inc(asyncDouble(3)) = inc(6) = 7
const bad = () => {
throw new Error("fail");
};
await compose(1, bad)
// Promise.reject(Error("fail"))
compose("hello")
// "hello"
Notes
- The right‑to‑left counterpart to pipe.
- It is intentionally minimal: no branching, no cleverness, no mutation.
- Because it never throws, it is safe to use inside validation, normalization, and result pipelines.
- Works seamlessly with tap, maybe, and tryOrDefault.