Skip to content
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

Conversation

andys8
Copy link
Contributor

@andys8 andys8 commented Sep 30, 2018

This PR changes the first argument, of the definition from any[] to a generic arg: T0.

I tried it out with a small file:

import compose from "compose-tiny"

const trim = (str: string) => str.trim()

const res0: (str: string) => string = compose(trim)
const res1: (str: string) => string  = compose(trim, trim)
const res2: (str: string) => string  = compose(trim, trim, trim)

Without the changes, the resulting function and the first function accepted any[]. The examples res0, res1 and res2 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

@hipstersmoothie
Copy link
Owner

@andys8 https://github.com/hipstersmoothie/compose-tiny#multiple-arguments

The first function in the composition should be able to take any amount of arguments. (...arg: any[]) => T11 results in a function the can take any amount of args and produce a T11. Is this not the case?

Is it just that I used just ...arg instead of ...args sometimes that made this confusing? I am definitely open to change.

@hipstersmoothie
Copy link
Owner

This seems to be related. The solution provided makes my head hurt lol

microsoft/TypeScript#26980

@andys8
Copy link
Contributor Author

andys8 commented Sep 30, 2018

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 any[] at the end for functions with large numbers of arguments. But a way to get this without typing out each variant in an overloaded function would be nice.

@andys8
Copy link
Contributor Author

andys8 commented Sep 30, 2018

There is the types/npm-ramda project, and it looks like they're generating definitions only. This is the result.

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 npm i -g types/npm-ramda#dist and looking into src/compose.d.ts

@hipstersmoothie
Copy link
Owner

hipstersmoothie commented Sep 30, 2018

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;

@andys8
Copy link
Contributor Author

andys8 commented Sep 30, 2018

I'd guess the types/npm-ramda function is limited to an 6 functions and a maximum arity of 6 for the first function. I guess this is what they chose as a sensible number to be supported.

If the any[] case adds value, it could be added additionally, but in their case, they probably decided there is no real value or use case (because everything is already any by default?).

@hipstersmoothie
Copy link
Owner

hipstersmoothie commented Sep 30, 2018

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 😮

@andys8
Copy link
Contributor Author

andys8 commented Sep 30, 2018

I think your example #26 (comment), the one I wrote #26 (comment) and the types/ramda definitions #26 (comment) are the same idea.

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 ... and any[] to avoid type errors in cases where the limit is reached? And how would the type definition look like, right? :)

Do we need multiple definitions again, or is it possible with one line?

@hipstersmoothie
Copy link
Owner

Yeah i agree they are all the same.

Should we add a generic fallback with spread operator ... and any[] to avoid type errors in cases where the limit is reached?

yes. i think this is necessary so that the function is usable past the defined limits.

And how do we type it?

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 any[] not sufficient?

@andys8
Copy link
Contributor Author

andys8 commented Sep 30, 2018

Assumed the definition of #26 (comment) is given.

How would the added fallback case with any[] look like?
(if my question is redundant, then I think I didn't understand the latest comment. Could you rephrase it? :))

Edit

Currently 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.

@hipstersmoothie
Copy link
Owner

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 FirstStep type in the compose (Step | FirstStep)[] then this will validate basically anything and allow function in the middle of the composition to have multiple args (an error).

  type Step = (arg: any) => any;
  type FirstStep = (...arg: any[]) => any;

  function Compose(
    ...functions: Step[]
  ): (...arg: any[]) => any;

The last hurdle of $1 is tough.

@andys8
Copy link
Contributor Author

andys8 commented Sep 30, 2018

I'm a bit confused by this, because FirstStep is never actually used.
I tried this, which could be same idea.

  function $<T1>(...fn0: ((...arg: any[]) => any | ((arg: any) => any))[]): (...arg: any[]) => any;

@andys8 andys8 force-pushed the improvement/type-safety-for-argument branch from 3b354ef to a0d3d44 Compare September 30, 2018 21:18
@hipstersmoothie
Copy link
Owner

FirstStep is not used because:

If you use the FirstStep type in the compose (Step | FirstStep)[] then this will validate basically anything and allow function in the middle of the composition to have multiple args (an error).

@andys8
Copy link
Contributor Author

andys8 commented Sep 30, 2018

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?

@andys8
Copy link
Contributor Author

andys8 commented Sep 30, 2018

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:

@hipstersmoothie
Copy link
Owner

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;

@andys8 andys8 force-pushed the improvement/type-safety-for-argument branch from a0d3d44 to 3aed605 Compare September 30, 2018 21:27
@hipstersmoothie
Copy link
Owner

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

@andys8 andys8 force-pushed the improvement/type-safety-for-argument branch from 3aed605 to 044f2f9 Compare September 30, 2018 21:35
@andys8
Copy link
Contributor Author

andys8 commented Sep 30, 2018

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;

@hipstersmoothie
Copy link
Owner

hipstersmoothie commented Sep 30, 2018

// 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

@andys8
Copy link
Contributor Author

andys8 commented Sep 30, 2018

Yeah, I fixed it to 10 functions max. Feel free to add more options with a commit or PR.
I've also opened an issue at npm-ramda to ask, why the limit of 6 was chosen: typed-typings/npm-ramda#414

@hipstersmoothie
Copy link
Owner

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

@andys8
Copy link
Contributor Author

andys8 commented Sep 30, 2018

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

@hipstersmoothie
Copy link
Owner

@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;

@andys8
Copy link
Contributor Author

andys8 commented Oct 1, 2018

So in the uncommon case somebody uses a lot of functions we'd allow wrongly composed functions?

If we add this as a fallback, fn1-10 in this example can again be typed with generics instead of any.

And: The library would be usable without the definition, if the user adds @ts-ignore, too.

@hipstersmoothie
Copy link
Owner

What do you think would be better? A fallback that isn't perfect or the ts-ignore?

@andys8
Copy link
Contributor Author

andys8 commented Oct 1, 2018

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 ;)

@hipstersmoothie hipstersmoothie added the patch create a patch release label Oct 1, 2018
@hipstersmoothie hipstersmoothie merged commit 645e2f6 into hipstersmoothie:master Oct 1, 2018
@andys8
Copy link
Contributor Author

andys8 commented Oct 1, 2018

Thanks @hipstersmoothie, I learned a lot in this PR.

@hipstersmoothie
Copy link
Owner

@andys8 Thank you! I learned a bunch as well. I was finally forced to grok generics lol

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
patch create a patch release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants