Skip to content

Commit

Permalink
feat: new function - pipe
Browse files Browse the repository at this point in the history
  • Loading branch information
GreatAuk committed Feb 23, 2023
1 parent 6476b38 commit 45ad96d
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 4 deletions.
2 changes: 0 additions & 2 deletions packages/core/src/compose.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ describe('compose', () => {
})

it('returns the given arguments if given no functions', () => {
// @ts-expect-error for test
expect(compose()(1, 2)).toEqual([1, 2])
// @ts-expect-error for test
expect(compose()(3)).toEqual([3])
})
})
5 changes: 3 additions & 2 deletions packages/core/src/compose.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
type Fn<Arg, V> = (args: Arg) => V
type Fn2<Args extends unknown[], V> = (...args: Args) => V
type Fn<Arg, R> = (args: Arg) => R
type Fn2<Args extends unknown[], R> = (...args: Args) => R

/**
* Composes single-argument functions from right to left. The rightmost
Expand All @@ -10,6 +10,7 @@ type Fn2<Args extends unknown[], V> = (...args: Args) => V
* to left. For example, `compose(f, g, h)` is identical to doing
* `(...args) => f(g(h(...args)))`.
*/
export function compose<Args extends unknown[]>(): (...args: Args) => Args
export function compose<Args extends unknown[], R1>(f1: Fn2<Args, R1>): (...args: Args) => R1
export function compose<Args extends unknown[], R1, R2>(f1: Fn<R2, R1>, f2: Fn2<Args, R2>): (...args: Args) => R1
export function compose<Args extends unknown[], R1, R2, R3>(f1: Fn<R2, R1>, f2: Fn<R3, R2>, f3: Fn2<Args, R3>): (...args: Args) => R1
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export * from './memoize'
export * from './objectKeys'
export * from './omit'
export * from './pick'
export * from './pipe'
export * from './randomInt'
export * from './randomString'
export * from './retry'
Expand Down
53 changes: 53 additions & 0 deletions packages/core/src/pipe.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { pipe } from './pipe'

describe('pipe', () => {
it('pipes from left to right', () => {
const double = (x: number) => x * 2
const square = (x: number) => x * x
expect(pipe(square)(5)).toBe(25)
expect(pipe(square, double)(5)).toBe(50)
expect(pipe(double, square, double)(5)).toBe(200)
})

it('pipes functions from left to right', () => {
const a = (next: (x: string) => string) => (x: string) => next(`${x}a`)
const b = (next: (x: string) => string) => (x: string) => next(`${x}b`)
const c = (next: (x: string) => string) => (x: string) => next(`${x}c`)
const final = (x: string) => x

expect(pipe(a, b, c)(final)('')).toBe('cba')
expect(pipe(b, c, a)(final)('')).toBe('acb')
expect(pipe(c, a, b)(final)('')).toBe('bac')
})

it('throws at runtime if argument is not a function', () => {
type sFunc = (x: number, y: number) => number
const square = (x: number) => x * x

expect(
() => pipe(false as unknown as sFunc, square)(1, 2))
.toThrow()
expect(
() => pipe(undefined as unknown as sFunc, square)(1, 2))
.toThrow()
expect(
() => pipe(true as unknown as sFunc, square)(1, 2))
.toThrow()
expect(
() => pipe(NaN as unknown as sFunc, square)(1, 2))
.toThrow()
expect(
() => pipe('42' as unknown as sFunc, square)(1, 2)).toThrow()
})

it('can be seeded with multiple arguments', () => {
const square = (x: number) => x * x
const add = (x: number, y: number) => x + y
expect(pipe(add, square)(1, 2)).toBe(9)
})

it('returns the given arguments if given no functions', () => {
expect(pipe()(1, 2)).toEqual([1, 2])
expect(pipe()(3)).toEqual([3])
})
})
25 changes: 25 additions & 0 deletions packages/core/src/pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
type Fn<Arg, V> = (args: Arg) => V
type Fn2<Args extends unknown[], V> = (...args: Args) => V

export function pipe<Args extends unknown[]>(): (...args: Args) => Args
export function pipe<Args extends unknown[], R1>(f1: Fn2<Args, R1>): (...args: Args) => R1
export function pipe<Args extends unknown[], R1, R2>(f1: Fn2<Args, R1>, f2: Fn<R1, R2>): (...args: Args) => R2
export function pipe<Args extends unknown[], R1, R2, R3>(f1: Fn2<Args, R1>, f2: Fn<R1, R2>, f3: Fn<R2, R3>): (...args: Args) => R3
export function pipe<Args extends unknown[], R1, R2, R3, R4>(f1: Fn2<Args, R1>, f2: Fn<R1, R2>, f3: Fn<R2, R3>, f4: Fn<R3, R4>): (...args: Args) => R4
export function pipe<Args extends unknown[], R1, R2, R3, R4, R5>(f1: Fn2<Args, R1>, f2: Fn<R1, R2>, f3: Fn<R2, R3>, f4: Fn<R3, R4>, f5: Fn<R4, R5>): (...args: Args) => R5
export function pipe<Args extends unknown[], R1, R2, R3, R4, R5, R6>(f1: Fn2<Args, R1>, f2: Fn<R1, R2>, f3: Fn<R2, R3>, f4: Fn<R3, R4>, f5: Fn<R4, R5>, f6: Fn<R5, R6>): (...args: Args) => R6
export function pipe<Args extends unknown[], R1, R2, R3, R4, R5, R6, R7>(f1: Fn2<Args, R1>, f2: Fn<R1, R2>, f3: Fn<R2, R3>, f4: Fn<R3, R4>, f5: Fn<R4, R5>, f6: Fn<R5, R6>, f7: Fn<R6, R7>): (...args: Args) => R7
export function pipe<Args extends unknown[], R1, R2, R3, R4, R5, R6, R7, R8>(f1: Fn2<Args, R1>, f2: Fn<R1, R2>, f3: Fn<R2, R3>, f4: Fn<R3, R4>, f5: Fn<R4, R5>, f6: Fn<R5, R6>, f7: Fn<R6, R7>, f8: Fn<R7, R8>): (...args: Args) => R8
export function pipe<Args extends unknown[], R1, R2, R3, R4, R5, R6, R7, R8, R9>(f1: Fn2<Args, R1>, f2: Fn<R1, R2>, f3: Fn<R2, R3>, f4: Fn<R3, R4>, f5: Fn<R4, R5>, f6: Fn<R5, R6>, f7: Fn<R6, R7>, f8: Fn<R7, R8>, f9: Fn<R8, R9>): (...args: Args) => R9
export function pipe<Args extends unknown[], R1, R2, R3, R4, R5, R6, R7, R8, R9, R10>(f1: Fn2<Args, R1>, f2: Fn<R1, R2>, f3: Fn<R2, R3>, f4: Fn<R3, R4>, f5: Fn<R4, R5>, f6: Fn<R5, R6>, f7: Fn<R6, R7>, f8: Fn<R7, R8>, f9: Fn<R8, R9>, f10: Fn<R9, R10>): (...args: Args) => R10
export function pipe<Args extends unknown[], R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11>(f1: Fn2<Args, R1>, f2: Fn<R1, R2>, f3: Fn<R2, R3>, f4: Fn<R3, R4>, f5: Fn<R4, R5>, f6: Fn<R5, R6>, f7: Fn<R6, R7>, f8: Fn<R7, R8>, f9: Fn<R8, R9>, f10: Fn<R9, R10>, f11: Fn<R10, R11>): (...args: Args) => R11
export function pipe<Args extends unknown[], R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12>(f1: Fn2<Args, R1>, f2: Fn<R1, R2>, f3: Fn<R2, R3>, f4: Fn<R3, R4>, f5: Fn<R4, R5>, f6: Fn<R5, R6>, f7: Fn<R6, R7>, f8: Fn<R7, R8>, f9: Fn<R8, R9>, f10: Fn<R9, R10>, f11: Fn<R10, R11>, f12: Fn<R11, R12>): (...args: Args) => R12
export function pipe(...fns: Function[]) {
if (fns.length === 0)
return <T extends unknown[]>(...args: T) => args

const fn = fns.shift()!
return function (this: any, ...args: unknown[]) {
return fns.reduce((acc, cur) => cur.call(this, acc), fn.call(this, ...args))
}
}

0 comments on commit 45ad96d

Please sign in to comment.