Add compose function and extend pipe with overloading
The best way to get type saftey with composition functions is via overloading. This does impose a hard limit on the humber of functions that can be composed. In this case, the limit is 10.
This commit is contained in:
parent
e696e5d9d2
commit
300a538314
@ -1,11 +1,171 @@
|
||||
import { type Either, left, right } from './either'
|
||||
import { none, type Option, some } from './option'
|
||||
|
||||
// Pipe function for composition
|
||||
export const pipe = <A, B, C>(a: A, ab: (a: A) => B, bc: (b: B) => C): C =>
|
||||
bc(ab(a))
|
||||
interface Pipe {
|
||||
<A>(value: A): A
|
||||
<A, B>(value: A, fn1: (a: A) => B): B
|
||||
<A, B, C>(value: A, fn1: (a: A) => B, fn2: (b: B) => C): C
|
||||
<A, B, C, D>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
): D
|
||||
<A, B, C, D, E>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
): E
|
||||
<A, B, C, D, E, F>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
fn5: (e: E) => F,
|
||||
): F
|
||||
<A, B, C, D, E, F, G>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
fn5: (e: E) => F,
|
||||
fn6: (f: F) => G,
|
||||
): G
|
||||
<A, B, C, D, E, F, G, H>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
fn5: (e: E) => F,
|
||||
fn6: (f: F) => G,
|
||||
fn7: (g: G) => H,
|
||||
): H
|
||||
<A, B, C, D, E, F, G, H, I>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
fn5: (e: E) => F,
|
||||
fn6: (f: F) => G,
|
||||
fn7: (g: G) => H,
|
||||
fn8: (h: H) => I,
|
||||
): I
|
||||
<A, B, C, D, E, F, G, H, I, J>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
fn5: (e: E) => F,
|
||||
fn6: (f: F) => G,
|
||||
fn7: (g: G) => H,
|
||||
fn8: (h: H) => I,
|
||||
fn9: (i: I) => J,
|
||||
): J
|
||||
<A, B, C, D, E, F, G, H, I, J, K>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
fn5: (e: E) => F,
|
||||
fn6: (f: F) => G,
|
||||
fn7: (g: G) => H,
|
||||
fn8: (h: H) => I,
|
||||
fn9: (i: I) => J,
|
||||
fn10: (j: J) => K,
|
||||
): K
|
||||
}
|
||||
|
||||
export const pipe: Pipe = (value: unknown, ...fns: Function[]): unknown => {
|
||||
return fns.reduce((acc, fn) => fn(acc), value)
|
||||
}
|
||||
|
||||
// Compose Function (reverse order)
|
||||
interface Compose {
|
||||
<A>(): (a: A) => A
|
||||
<A, B>(fn1: (a: A) => B): (a: A) => B
|
||||
<A, B, C>(fn2: (b: B) => C, fn1: (a: A) => B): (a: A) => C
|
||||
<A, B, C, D>(
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => D
|
||||
<A, B, C, D, E>(
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => E
|
||||
<A, B, C, D, E, F>(
|
||||
fn5: (e: E) => F,
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => F
|
||||
<A, B, C, D, E, F, G>(
|
||||
fn6: (f: F) => G,
|
||||
fn5: (e: E) => F,
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => G
|
||||
<A, B, C, D, E, F, G, H>(
|
||||
fn7: (g: G) => H,
|
||||
fn6: (f: F) => G,
|
||||
fn5: (e: E) => F,
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => H
|
||||
<A, B, C, D, E, F, G, H, I>(
|
||||
fn8: (h: H) => I,
|
||||
fn7: (g: G) => H,
|
||||
fn6: (f: F) => G,
|
||||
fn5: (e: E) => F,
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => I
|
||||
<A, B, C, D, E, F, G, H, I, J>(
|
||||
fn9: (i: I) => J,
|
||||
fn8: (h: H) => I,
|
||||
fn7: (g: G) => H,
|
||||
fn6: (f: F) => G,
|
||||
fn5: (e: E) => F,
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => J
|
||||
<A, B, C, D, E, F, G, H, I, J, K>(
|
||||
fn10: (j: J) => K,
|
||||
fn9: (i: I) => J,
|
||||
fn8: (h: H) => I,
|
||||
fn7: (g: G) => H,
|
||||
fn6: (f: F) => G,
|
||||
fn5: (e: E) => F,
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => K
|
||||
}
|
||||
|
||||
export const compose: Compose = (...fns: Function[]) => {
|
||||
return (value: unknown) => fns.reduceRight((acc, fn) => fn(acc), value)
|
||||
}
|
||||
|
||||
// TryCatch helper using Either
|
||||
export const tryCatch = <A>(f: () => A): Either<A, Error> => {
|
||||
try {
|
||||
return right(f())
|
||||
@ -14,6 +174,5 @@ export const tryCatch = <A>(f: () => A): Either<A, Error> => {
|
||||
}
|
||||
}
|
||||
|
||||
// Option from nullable
|
||||
export const fromNullable = <A>(a: A | null | undefined): Option<A> =>
|
||||
a == null ? none : some(a)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user