-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TS defintion produces a typesafe defintion including first argument #26
TS defintion produces a typesafe defintion including first argument #26
Conversation
@andys8 https://github.com/hipstersmoothie/compose-tiny#multiple-arguments The first function in the composition should be able to take any amount of arguments. Is it just that I used just |
This seems to be related. The solution provided makes my head hurt lol |
You're right. The current solution won't work for this case. Doing it manually it would look something like this. Example for composing two functions function Compose<T0, T1, T2>(
fn1: (a: T1) => T2,
fn0: (arg: T0) => T1
): (arg: T0) => T2;
function Compose<A0, A1, T1, T2>(
fn1: (a: T1) => T2,
fn0: (a0: A0, a1: A1) => T1
): (a0: A0, a1: A1) => T2;
function Compose<A0, A1, A2, T1, T2>(
fn1: (a: T1) => T2,
fn0: (a0: A0, a1: A1, a2: A2) => T1
): (a0: A0, a1: A1, a2: A2) => T2;
// TODO: More than three arguments in first function
function Compose<A0, T1, T2>(
fn1: (a: T1) => T2,
fn0: (...arg: A0[]) => T1
): (...arg: A0[]) => T2;
function Compose<T1, T2>(
fn1: (a: T1) => T2,
fn0: (...arg: any[]) => T1
): (...arg: any[]) => T2; There could be cases with |
There is the declare const compose: typeof $;
/**
* Performs right-to-left function composition. The rightmost function may have
* any arity; the remaining functions must be unary.
*
* **Note:** The result of compose is not automatically curried.
*
* @func
* @memberOf R
* @since v0.1.0
* @category Function
* @sig ((y -> z), (x -> y), ..., (o -> p), ((a, b, ..., n) -> o)) -> ((a, b, ..., n) -> z)
* @param {...Function} ...functions The functions to compose
* @return {Function}
* @see R.pipe
* @example
*
* var classyGreeting = (firstName, lastName) => "The name's " + lastName + ", " + firstName + " " + lastName
* var yellGreeting = R.compose(R.toUpper, classyGreeting);
* yellGreeting('James', 'Bond'); //=> "THE NAME'S BOND, JAMES BOND"
*
* R.compose(Math.abs, R.add(1), R.multiply(2))(-4) //=> 7
*
* @symb R.compose(f, g, h)(a, b) = f(g(h(a, b)))
*/
declare function $<R1>(fn1: () => R1): () => R1;
declare function $<V1, R1>(fn1: (v1: V1) => R1): (v1: V1) => R1;
declare function $<V1, V2, R1>(fn1: (v1: V1, v2: V2) => R1): (v1: V1, v2: V2) => R1;
declare function $<V1, V2, V3, R1>(fn1: (v1: V1, v2: V2, v3: V3) => R1): (v1: V1, v2: V2, v3: V3) => R1;
declare function $<V1, V2, V3, V4, R1>(fn1: (v1: V1, v2: V2, v3: V3, v4: V4) => R1): (v1: V1, v2: V2, v3: V3, v4: V4) => R1;
declare function $<V1, V2, V3, V4, V5, R1>(fn1: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => R1): (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => R1;
declare function $<V1, V2, V3, V4, V5, V6, R1>(fn1: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R1): (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R1;
declare function $<R1, R2>(fn2: (v: R1) => R2, fn1: () => R1): () => R2;
declare function $<V1, R1, R2>(fn2: (v: R1) => R2, fn1: (v1: V1) => R1): (v1: V1) => R2;
declare function $<V1, V2, R1, R2>(fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2) => R1): (v1: V1, v2: V2) => R2;
declare function $<V1, V2, V3, R1, R2>(fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3) => R1): (v1: V1, v2: V2, v3: V3) => R2;
declare function $<V1, V2, V3, V4, R1, R2>(fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4) => R1): (v1: V1, v2: V2, v3: V3, v4: V4) => R2;
declare function $<V1, V2, V3, V4, V5, R1, R2>(fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => R1): (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => R2;
declare function $<V1, V2, V3, V4, V5, V6, R1, R2>(fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R1): (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R2;
declare function $<R1, R2, R3>(fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: () => R1): () => R3;
declare function $<V1, R1, R2, R3>(fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1) => R1): (v1: V1) => R3;
declare function $<V1, V2, R1, R2, R3>(fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2) => R1): (v1: V1, v2: V2) => R3;
declare function $<V1, V2, V3, R1, R2, R3>(fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3) => R1): (v1: V1, v2: V2, v3: V3) => R3;
declare function $<V1, V2, V3, V4, R1, R2, R3>(fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4) => R1): (v1: V1, v2: V2, v3: V3, v4: V4) => R3;
declare function $<V1, V2, V3, V4, V5, R1, R2, R3>(fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => R1): (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => R3;
declare function $<V1, V2, V3, V4, V5, V6, R1, R2, R3>(fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R1): (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R3;
declare function $<R1, R2, R3, R4>(fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: () => R1): () => R4;
declare function $<V1, R1, R2, R3, R4>(fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1) => R1): (v1: V1) => R4;
declare function $<V1, V2, R1, R2, R3, R4>(fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2) => R1): (v1: V1, v2: V2) => R4;
declare function $<V1, V2, V3, R1, R2, R3, R4>(fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3) => R1): (v1: V1, v2: V2, v3: V3) => R4;
declare function $<V1, V2, V3, V4, R1, R2, R3, R4>(fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4) => R1): (v1: V1, v2: V2, v3: V3, v4: V4) => R4;
declare function $<V1, V2, V3, V4, V5, R1, R2, R3, R4>(fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => R1): (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => R4;
declare function $<V1, V2, V3, V4, V5, V6, R1, R2, R3, R4>(fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R1): (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R4;
declare function $<R1, R2, R3, R4, R5>(fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: () => R1): () => R5;
declare function $<V1, R1, R2, R3, R4, R5>(fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1) => R1): (v1: V1) => R5;
declare function $<V1, V2, R1, R2, R3, R4, R5>(fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2) => R1): (v1: V1, v2: V2) => R5;
declare function $<V1, V2, V3, R1, R2, R3, R4, R5>(fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3) => R1): (v1: V1, v2: V2, v3: V3) => R5;
declare function $<V1, V2, V3, V4, R1, R2, R3, R4, R5>(fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4) => R1): (v1: V1, v2: V2, v3: V3, v4: V4) => R5;
declare function $<V1, V2, V3, V4, V5, R1, R2, R3, R4, R5>(fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => R1): (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => R5;
declare function $<V1, V2, V3, V4, V5, V6, R1, R2, R3, R4, R5>(fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R1): (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R5;
declare function $<R1, R2, R3, R4, R5, R6>(fn6: (v: R5) => R6, fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: () => R1): () => R6;
declare function $<V1, R1, R2, R3, R4, R5, R6>(fn6: (v: R5) => R6, fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1) => R1): (v1: V1) => R6;
declare function $<V1, V2, R1, R2, R3, R4, R5, R6>(fn6: (v: R5) => R6, fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2) => R1): (v1: V1, v2: V2) => R6;
declare function $<V1, V2, V3, R1, R2, R3, R4, R5, R6>(fn6: (v: R5) => R6, fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3) => R1): (v1: V1, v2: V2, v3: V3) => R6;
declare function $<V1, V2, V3, V4, R1, R2, R3, R4, R5, R6>(fn6: (v: R5) => R6, fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4) => R1): (v1: V1, v2: V2, v3: V3, v4: V4) => R6;
declare function $<V1, V2, V3, V4, V5, R1, R2, R3, R4, R5, R6>(fn6: (v: R5) => R6, fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => R1): (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => R6;
declare function $<V1, V2, V3, V4, V5, V6, R1, R2, R3, R4, R5, R6>(fn6: (v: R5) => R6, fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R1): (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R6;
export = compose; Can be found with |
To me it looks like that doesn't really result in an any arity first function. the following lin suggests the greatest amount of args in the first function is 6 and the greatest amount of functions 6. Does this in fact support an any arity first function and an unlimited amount of functions? declare function $<V1, V2, V3, V4, V5, V6, R1, R2, R3, R4, R5, R6>(fn6: (v: R5) => R6, fn5: (v: R4) => R5, fn4: (v: R3) => R4, fn3: (v: R2) => R3, fn2: (v: R1) => R2, fn1: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R1): (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => R6; |
I'd guess the If the |
During my testing if i deleted the any[] case it would error. saying that the function only excepted the greatest amount of params defined. I would be open to a pr that does this for every case. function Compose<V1, T1, T2>(fn1: (a: T1) => T2, fn0: (a1: V1) => T1 ): (a1: V1) => T2;
function Compose<V1, V2, T1, T2>(fn1: (a: T1) => T2, fn0: (a1: V1, a2: V2) => T1 ): (a1: V1, a2: V2) => T2;
function Compose<V1, V2, V3, T1, T2>(fn1: (a: T1) => T2, fn0: (a1: V1, a2: V2, a3: V3) => T1 ): (a1: V1, a2: V2, a3: V3) => T2;
function Compose<V1, V2, V3, V4, T1, T2>(fn1: (a: T1) => T2, fn0: (a1: V1, a2: V2, a3: V3, a4: V4) => T1 ): (a1: V1, a2: V2, a3: V3, a4: V4) => T2;
function Compose<V1, V2, V3, V4, V5, T1, T2>(fn1: (a: T1) => T2, fn0: (a1: V1, a2: V2, a3: V3, a4: V4, a5: V5) => T1 ): (a1: V1, a2: V2, a3: V3, a4: V4, a5: V5) => T2;
function Compose<V1, V2, V3, V4, V5, V6, T1, T2>(fn1: (a: T1) => T2, fn0: (a1: V1, a2: V2, a3: V3, a4: V4, a5: V5, a6: V6) => T1 ): (a1: V1, a2: V2, a3: V3, a4: V4, a5: V5, a6: V6) => T2; I would turn off prettier formatting for the .d.ts file though. its gonna get a bit hectic. 6 args * 10 functions = 60 type function declarations 😮 |
I think your example #26 (comment), the one I wrote #26 (comment) and the So we could use the generated types, add a reference to mark where it's taken from, and focus on the question: Should we add a generic fallback with spread operator Do we need multiple definitions again, or is it possible with one line? |
Yeah i agree they are all the same.
yes. i think this is necessary so that the function is usable past the defined limits.
I don't think there is a way to do this. From the issues I've linked there only seems to be some crazy hard to understand examples. and they are frowned upon. Is |
Assumed the definition of #26 (comment) is given. How would the added fallback case with EditCurrently I think we need again multiple definitions, to support it. function $<T1>(fn0: (...arg: any[]) => T1): (...arg: any[]) => T1;
function $<T1, T2>(fn1: (a: T1) => T2, fn0: (...arg: any[]) => T1): (...arg: any[]) => T2;
function $<T1, T2, T3>(fn2: (a: T2) => T3, fn1: (a: T1) => T2, fn0: (...arg: any[]) => T1): (...arg: any[]) => T3; ... and so on. |
The current setup catches the big case pretty well. The only caveat is that it doesn't allow the first function to have multiple parameters. ($1) If you use the
The last hurdle of $1 is tough. |
I'm a bit confused by this, because function $<T1>(...fn0: ((...arg: any[]) => any | ((arg: any) => any))[]): (...arg: any[]) => any; |
3b354ef
to
a0d3d44
Compare
FirstStep is not used because:
|
I pushed an update to this PR. Can you have a look if there are cases, where this won't type-check or work as expected? |
Ah, okay, I get it. This is now the case. I think to allow unlimited functions and arity in the first function, this is the compromise to make. Alternatives are:
|
import compose from 'compose-tiny';
const add = (x: number, y: number) => x + y;
const add1 = (x: number) => add(1, x);
const print = (x: number) => `This is the number ${x}`;
compose(add1, print); // gives no error with the following The following allows anything basically. function $<T1>(...fn0: ((...arg: any[]) => any | ((arg: any) => any))[]): (...arg: any[]) => any; |
a0d3d44
to
3aed605
Compare
I think the compromise that has to be made is with > 10 functions the first function is going to error if it has multiple args |
3aed605
to
044f2f9
Compare
How about this? I hope there is no silly mistake in there 😄 // Support unlimited arity with any in first function and higher number of composed functions
function $<T1>(fn0: (...arg: any[]) => T1): (...arg: any[]) => T1;
function $<T1, T2>(fn1: (a: T1) => T2, fn0: (...arg: any[]) => T1): (...arg: any[]) => T2;
function $<T1, T2, T3>(fn2: (a: T2) => T3, fn1: (a: T1) => T2, fn0: (...arg: any[]) => T1): (...arg: any[]) => T3;
function $<T1, T2, T3, T4>(fn3: (a: T3) => T4, fn2: (a: T2) => T3, fn1: (a: T1) => T2, fn0: (...arg: any[]) => T1): (...arg: any[]) => T4;
function $<T1, T2, T3, T4, T5>(fn4: (a: T4) => T5, fn3: (a: T3) => T4, fn2: (a: T2) => T3, fn1: (a: T1) => T2, fn0: (...arg: any[]) => T1): (...arg: any[]) => T5;
function $<T1, T2, T3, T4, T5, T6>(fn5: (a: T5) => T6, fn4: (a: T4) => T5, fn3: (a: T3) => T4, fn2: (a: T2) => T3, fn1: (a: T1) => T2, fn0: (...arg: any[]) => T1): (...arg: any[]) => T6;
function $<T1, T2, T3, T4, T5, T6, T7>(fn6: (a: T6) => T7, fn5: (a: T5) => T6, fn4: (a: T4) => T5, fn3: (a: T3) => T4, fn2: (a: T2) => T3, fn1: (a: T1) => T2, fn0: (...arg: any[]) => T1): (...arg: any[]) => T7;
function $<T1, T2, T3, T4, T5, T6, T7, T8>(fn7: (a: T7) => T8, fn6: (a: T6) => T7, fn5: (a: T5) => T6, fn4: (a: T4) => T5, fn3: (a: T3) => T4, fn2: (a: T2) => T3, fn1: (a: T1) => T2, fn0: (...arg: any[]) => T1): (...arg: any[]) => T8;
function $<T1, T2, T3, T4, T5, T6, T7, T8, T9>(fn8: (a: T8) => T9, fn7: (a: T7) => T8, fn6: (a: T6) => T7, fn5: (a: T5) => T6, fn4: (a: T4) => T5, fn3: (a: T3) => T4, fn2: (a: T2) => T3, fn1: (a: T1) => T2, fn0: (...arg: any[]) => T1): (...arg: any[]) => T9;
function $<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(fn9: (a: T9) => T10, fn8: (a: T8) => T9, fn7: (a: T7) => T8, fn6: (a: T6) => T7, fn5: (a: T5) => T6, fn4: (a: T4) => T5, fn3: (a: T3) => T4, fn2: (a: T2) => T3, fn1: (a: T1) => T2, fn0: (...arg: any[]) => T1): (...arg: any[]) => T10; |
// Expected 1-10 arguments, but got 13. import compose from 'compose-tiny';
const add = (x: number, y: number) => x + y;
const add1 = (x: number) => add(1, x);
const print = (x: number) => `This is the number ${x}`;
compose(
add1,
add1,
add1,
add1,
add1,
add1,
add1,
add1,
add1,
add1,
add1,
add1,
print
); this is the case where we probably wont be able to support print as the last function but it could be add1 |
Yeah, I fixed it to |
All i would want to add it this at the end of the file function $( ...functions: ((arg: any) => any)[] ): (a: any) => any; It fails with #26 (comment) but allows any amount of functions and still fails in normal range |
I think if this get's added, it would allow non-matching function types in common cases. Composing those functions would be a user mistake. compose((a:number) => a + 1, (a:string) => a.trim()) // OK: (a: any) => any
compose((a:string) => a.trim(), (a:number) => a + 1, (a:string) => a.trim()) // OK: (a: any) => any |
@andys8 How about this? define the first 10 functions in the uncommon case so that it only matches past the common cases. function $(fn10: (a: any) => any, fn9: (a: any) => any, fn8: (a: any) => any, fn7: (a: any) => any, fn6: (a: any) => any, fn5: (a: any) => any, fn4: (a: any) => any, fn3: (a: any) => any, fn2: (a: any) => any, fn1: (a: any) => any, ...fn0: ((...arg: any[]) => any)[]): (...arg: any[]) => any; |
So in the uncommon case somebody uses a lot of functions we'd allow wrongly composed functions? If we add this as a fallback, And: The library would be usable without the definition, if the user adds |
What do you think would be better? A fallback that isn't perfect or the ts-ignore? |
I'd prefer not having the potentially wrong fallback then. Having a look at Ramda again, it looks like all type definitions have a an arbitrary limit. If somebody has a usecase with more than 10 functions, it can be added in the future, too ;) |
Thanks @hipstersmoothie, I learned a lot in this PR. |
@andys8 Thank you! I learned a bunch as well. I was finally forced to grok generics lol |
This PR changes the first argument, of the definition from
any[]
to a genericarg: T0
.I tried it out with a small file:
Without the changes, the resulting function and the first function accepted
any[]
. The examplesres0
,res1
andres2
are of type(str: string) => string
. This would also be inferred, if the type annotation would be missing.Feedback
Besides the positive changes: Will this PR have negative consequences? Is there something I missed, that made the definitions different before?
Relating #18