Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions docs/pages/function/pipe.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# pipe

Pipes a value through multiple functions from left to right. Each function receives the output of the previous one.

### Import

```typescript copy
import { pipe } from '@fullstacksjs/toolbox';
```

### Signature

```typescript copy
function pipe<A, B>(value: A, ...fns: Function[]): B;
```

### Examples

```typescript copy
const add = (x: number) => x + 1;
const double = (x: number) => x * 2;
const toString = (x: number) => x.toString();

const result = pipe(5, add, double, toString);
console.log(result); // ((5 + 1) * 2).toString() => "12"


const split = (str: string) => str.split(' ');
const getLength = (arr: string[]) => arr.length;
const isEven = (num: number) => num % 2 === 0;

const hasEvenWordCount = pipe('hello world from typescript', split, getLength, isEven);
console.log(hasEvenWordCount); // true (4 words)
```
1 change: 1 addition & 0 deletions src/function/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export { debounce } from './debounce.ts';
export { formatSeconds } from './formatSeconds.ts';
export { noop } from './noop.ts';
export { not } from './not.ts';
export { pipe } from './pipe.ts';
export { sleep } from './sleep.ts';
export { throttle } from './throttle.ts';
export { tryOr } from './tryOr.ts';
53 changes: 53 additions & 0 deletions src/function/pipe.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { pipe } from './pipe';

describe('pipe', () => {
it('should pipe value through functions from left to right', () => {
const add = (x: number) => x + 1;
const multiply = (x: number) => x * 2;
const result = pipe(5, add, multiply);

expect(result).toBe(12);
});

it('should handle single function', () => {
const square = (x: number) => x * x;
const result = pipe(4, square);

expect(result).toBe(16);
});

it('should work with same-type functions', () => {
const trim = (s: string) => s.trim();
const toUpper = (s: string) => s.toUpperCase();
const result = pipe(' hey ', trim, toUpper);

expect(result).toBe('HEY');
});

it('should work with different types', () => {
const toNumber = (s: string) => Number.parseInt(s, 10);
const double = (n: number) => n * 2;
const toString = (n: number) => n.toString();
const result = pipe('5', toNumber, double, toString);

expect(result).toBe('10');
});

it('should pipe through 3+ functions', () => {
const add1 = (x: number) => x + 1;
const multiply2 = (x: number) => x * 2;
const subtract3 = (x: number) => x - 3;
const result = pipe(5, add1, multiply2, subtract3);

expect(result).toBe(9); // ((5 + 1) * 2) - 3 = 9
});

it('should preserve type safety across transformations', () => {
const split = (s: string) => s.split(',');
const count = (arr: string[]) => arr.length;
const isEven = (n: number) => n % 2 === 0;

expect(pipe('a,b,c', split, count, isEven)).toBe(false);
expect(pipe('a,b,c,d', split, count, isEven)).toBe(true);
});
});
66 changes: 66 additions & 0 deletions src/function/pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
type UnknownFunction = (arg: unknown) => unknown;

/**
* Pipes a value through a series of functions, applying each function to the result of the previous one.
* The value flows from left to right through the function pipeline.
*
* @template A - The type of the initial value
* @template B - The type after the first transformation
* @param {A} value - The initial value to pipe through the functions
* @param {...Function} fns - The functions to apply in sequence
* @returns {B} The final transformed value
*
* @example
*
* const add = (x: number) => x + 1;
* const double = (x: number) => x * 2;
* const toString = (x: number) => x.toString();
*
* const result = pipe(5, add, double, toString);
* console.log(result); // ((5 + 1) * 2).toString() => "12"
*
* @example
*
* const split = (str: string) => str.split(' ');
* const getLength = (arr: string[]) => arr.length;
* const isEven = (num: number) => num % 2 === 0;
*
* const hasEvenWordCount = pipe('hello world from typescript', split, getLength, isEven);
* console.log(hasEvenWordCount); // true (4 words)
*/
export function pipe<A, B>(value: A, ab: (a: A) => B): B;
export function pipe<A, B, C>(value: A, ab: (a: A) => B, bc: (b: B) => C): C;
export function pipe<A, B, C, D>(
value: A,
ab: (a: A) => B,
bc: (b: B) => C,
cd: (c: C) => D,
): D;
export function pipe<A, B, C, D, E>(
value: A,
ab: (a: A) => B,
bc: (b: B) => C,
cd: (c: C) => D,
de: (d: D) => E,
): E;
export function pipe<A, B, C, D, E, F>(
value: A,
ab: (a: A) => B,
bc: (b: B) => C,
cd: (c: C) => D,
de: (d: D) => E,
ef: (e: E) => F,
): F;
export function pipe<A, B, C, D, E, F, G>(
value: A,
ab: (a: A) => B,
bc: (b: B) => C,
cd: (c: C) => D,
de: (d: D) => E,
ef: (e: E) => F,
fg: (f: F) => G,
): G;

export function pipe(value: unknown, ...fns: UnknownFunction[]): unknown {
return fns.reduce((acc, fn) => fn(acc), value);
}