Documentation Index
Fetch the complete documentation index at: https://docs.cbrock.dev/llms.txt
Use this file to discover all available pages before exploring further.
pipe and flow are utility functions for composing sequences of transformations. Both are fully typed: each function’s return type becomes the next function’s argument type, with no loss of type information.
import { pipe, flow } from 'ts-chas/pipe';
Both support up to 10 functions in a single call. If you need more, break your pipeline into named intermediate steps.
pipe(value, ...fns)
Takes an initial value and passes it through a sequence of functions left-to-right. Returns the output of the final function.
import { pipe } from 'ts-chas/pipe';
const add5 = (x: number) => x + 5;
const double = (x: number) => x * 2;
const result = pipe(10, add5, double);
// add5(10) = 15, double(15) = 30
// result: 30
Each function’s return type informs the next function’s parameter type:
const result = pipe(
'hello world',
(s) => s.split(' '), // string → string[]
(words) => words.length, // string[] → number
(n) => `Word count: ${n}` // number → string
);
// 'Word count: 2'
flow(...fns)
Composes functions into a single reusable function without an initial value. Call the result later with your input. Think of it as a named, reusable pipeline.
import { flow } from 'ts-chas/pipe';
const add5 = (x: number) => x + 5;
const double = (x: number) => x * 2;
const calculate = flow(add5, double);
// calculate is typed as (x: number) => number
calculate(10); // 30
calculate(0); // 10
flow is useful for defining transforms you want to pass as callbacks or store as constants:
const normalizeEmail = flow(
(s: string) => s.trim(),
(s) => s.toLowerCase(),
(s) => s.replace(/\s+/g, '')
);
[' Alice@Example.COM ', ' BOB@TEST.ORG'].map(normalizeEmail);
// ['alice@example.com', 'bob@test.org']
Built-in .pipe() on Result and ResultAsync
Result and ResultAsync have their own .pipe() method. It behaves like pipe, but is Result-aware: if any step returns an Err, the chain short-circuits and subsequent functions are skipped.
import { chas } from 'ts-chas';
const add5 = (x: number) => x + 5;
const double = (x: number) => x * 2;
const result = chas.ok(10).pipe(add5, double);
// Ok(30)
const errResult = chas.err('something went wrong').pipe(add5, double);
// Err('something went wrong') — add5 and double are never called
This also works on ResultAsync:
const asyncResult = chas.okAsync(10).pipe(add5, double);
// ResultAsync<30, never>
Complete example
This example shows all three forms working together in a data transformation pipeline:
import { pipe, flow } from 'ts-chas/pipe';
import { chas } from 'ts-chas';
interface RawProduct {
name: string;
price_cents: number;
tags: string | null;
}
interface Product {
name: string;
priceUsd: number;
tags: string[];
}
// Reusable transforms defined with flow
const centsToUsd = flow(
(cents: number) => cents / 100,
(usd) => Math.round(usd * 100) / 100
);
const parseTags = (raw: string | null): string[] =>
raw ? raw.split(',').map(t => t.trim()).filter(Boolean) : [];
// A single-value transformation using pipe
function toProduct(raw: RawProduct): Product {
return pipe(
raw,
(r) => ({ ...r, priceUsd: centsToUsd(r.price_cents) }),
(r) => ({ ...r, tags: parseTags(r.tags) }),
({ name, priceUsd, tags }) => ({ name, priceUsd, tags })
);
}
// Result-aware pipeline using the built-in .pipe() on Result
function parseAndTransform(input: unknown): chas.Result<Product, string> {
return chas
.tryCatch(
() => input as RawProduct,
() => 'Invalid product data'
)
.pipe(
(raw) => ({ ...raw, name: raw.name.trim() }),
toProduct
);
}
// Usage
const result = parseAndTransform({ name: ' Widget ', price_cents: 1999, tags: 'sale, new' });
if (result.isOk()) {
console.log(result.value);
// { name: 'Widget', priceUsd: 19.99, tags: ['sale', 'new'] }
}