Here's what I've done so far to implement function composition sequence using Array.reduce
.
type Compose = <A, B, C>
(f: (a: A) => B, g: (b: B) => C) =>
(c: A) => C;
const compose: Compose =
(f, g) => a => g(f(a));
type C_ =
(fs: Array<(a: any) => any>) =>
(c: unknown) => unknown
const c_: C_ =
fs => fs.reduce(compose);
//-------------------------------
const f = (a: number) => a * 2;
const g = (a: number) => a + 1;
const h = (a: number) => a * 5;
const s = (a: number) => a.toString();
//-------------------------------
const composedF = c_([f, g, h, s]);
console.log(
composedF(1)
); // "15"
I needed to write
type C_ =
(fs: Array<(a: any) => any>) =>
(c: unknown) => unknown
with any
type and if changed to unknown
for instance,
type C_ =
(fs: Array<(a: unknown) => unknown>) =>
(c: unknown) => unknown
I have an error:
Type '(a: number) => number' is not assignable to type '(a: unknown) => unknown'.
Types of parameters 'a' and 'a' are incompatible.
Type 'unknown' is not assignable to type 'number'.
So, my question is, is there any way not to use any
in this code?
Edit:
Based on @jcalz work, now I tried to implement p_
as pipeline application on the composition of functions.
I thought it's trivial, but for some unknown reason, the type doesn't work and allows the illegal operation against my expectation.
type compose = <A, B, C>
(f: (a: A) => B, g: (b: B) => C) =>
(c: A) => C;
const compose: compose =
(f, g) => a => g(f(a));
type Lookup<T, K extends keyof any, Else = never> = K extends keyof T ? T[K] : Else
type Tail<T extends any[]> = T extends [any, ...infer R] ? R : never;
type Func1 = (a: any) => any;
type aType<F, Else = never> = F extends (a: infer A) => any ? A : Else;
type AsChain<F extends [Func1, ...Func1[]], G extends Func1[] = Tail<F>> =
{ [K in keyof F]: (a: aType<F[K]>) => aType<Lookup<G, K, any>, any> };
type Last<T extends any[]> = T extends [...infer F, infer L] ? L : never;
type LaxReturnType<F> = F extends (...as: any) => infer R ? R : never;
type c_ = <F extends
[(a: any) => any, ...Array<(a: any) => any>]>
(fs: F & AsChain<F>) =>
(a: aType<F[0]>) => LaxReturnType<Last<F>>;
const c_: c_ = fs => fs.reduce(compose);
type p_ = <F extends
[(a: any) => any, ...Array<(a: any) => any>]>
(a: aType<(F & AsChain<F, Tail<F>>)[0], never>) =>
(fs: F & AsChain<F>) => LaxReturnType<Last<F & AsChain<F, Tail<F>>>>;
const p_: p_ = a => fs => {
const composedF = c_(fs)
return composedF(a)
};
//-------------------------------
const f = (a: number) => a * 2;
const g = (a: number) => a + 1;
const h = (a: number) => a * 5;
const s = (a: number) => a.toString();
//-------------------------------
const composedF = c_([f, g, h, s]);
console.log(
composedF(1)
); // "15"
console.log(
p_(1)([f, s, g, h, s])
); // type error expected, but illegaly works as "105"