I'm working on implementing a function that can add a chainable method for function composition. Here's what I have so far:
Also see: TypeScript playground
{
const F = <T, U>(f: (a: T) => U) => {
type F = {
compose: <V, >(g: (b: U) => V) => (((a: T) => V) & F);
};
return Object.defineProperty(f, "compose", {
configurable: true,
value: <V,>(g: (b: U) => V) => F((a: T) => g(f(a)))
}) as ((a: T) => U) & F
};
//--------------------------------
const f1 = (a: number) => a + 1;
const f2 = (a: number) => a.toString();
const identity = <T,>(a: T) => a;
//--------------------------------
const F2 = F(f2);
// ((a: number) => string) & F // good
const F12 = F(f1).compose(f2);
// ((a: number) => string) & F // good
//--------------------------------
const F2i = (F2).compose(identity);
// ((a: number) => string) & F // good
const f12i = (F12).compose(identity);
// ((a: number) => number) & F // bad why??
//--------------------------------
const fi1 = F(identity).compose(f1);
/* ts(2345) error
Argument of type '(a: number) => number' is not assignable to parameter of type '(b: unknown) => number'.
Types of parameters 'a' and 'b' are incompatible.
Type 'unknown' is not assignable to type 'number'.
const f1: (a: number) => number
*/
}
In this example code, we have three basic functions; f1
, f2
, and identity
.
F
is the function that extends a specified function with a method for function composition.
I've made it work somehow, but I've encountered at least two issues.
1.
When using F
for f2
, the type of F2
(
((a: number) => string) & F
) is as expected. Similarly, when using F
for f1
and composing with f2
, the type of F12
is also as expected.
However, the type of (F12).compose(identity)
turns out to be
((a: number) => number) & F
, which is unexpected. I've been trying to figure out why this happens without success.
If you have any advice, I'd appreciate it. Thanks!
EDIT:
Please note that the functions should not be wrapped in an Object, and my goal is to provide a compose method directly to the functions:
const f = (a: number) => a + 1;
const fg = f.compose(g);
//not
{
f: f,
compose:someFunction
}
EDIT: Regarding issue #2, I created a separate question based on comments from @jcalz:
Is there any workaround for ts(2345) error for TypeScript lacks higher kinded types?
2.
I'm encountering a ts(2345) error, and the error message is unclear to me, making it difficult to resolve. Any ideas on how to fix this would be greatly appreciated.