简体   繁体   中英

Is it possible to flow type a function that returns another function that may be curried?

Let's say you have a function that returns another function, and that function that is returned may or may not be curried. Can this be flow typed? I'm guessing that the answer may be no.

Here's an example:

function getAddFn(curried) {
  if (curried === true) {
    return function add(x) {
      return function(y) {
        return x + y;
      }
    }
  }

  return function add(x, y) { return x + y };
}

const add = getAddFn(false);

add(2, 3)

For this code, flow gives the following error:

add(2, 3)
           ^ Cannot call `add` because no more than 1 argument is expected by function [1].
References:
3:     return function add(x) {
              ^ [1]

Essentially, flow is unable to determine which function style has been returned. It's also not possible to type the variable that receives the function, eg:

const add: (number, number) => number = getAddFn(false);

In this case, flow complains:

13: const add: (number, number) => number = getAddFn(false);
                                            ^ Cannot assign `getAddFn(...)` to `add` because function [1] is incompatible with number [2] in the return value.
References:
4:       return function(y) {
                ^ [1]
13: const add: (number, number) => number = getAddFn(false);
                                   ^ [2]

Can anyone explain if this is possible? Or if not, clearly explain why? I can see that the problem could be related to the fact that the value of the boolean passed into getAddFn is not known until runtime.

While it's true that Flow doesn't know whether curried is true or false, you're hitting a much more fundamental limitation than that. In flow, a union type x : A | B x : A | B doesn't mean that x is either A or B, and that Flow doesn't have enough information to distinguish them. It means that x has the type A | B A | B . The type A | B A | B can only be consumed in two ways:

  • by passing it to something which can accept both an A and a B
  • by passing it to something which can separate out an A from a B, such as an if statement

The distinction is subtle, but it gets clearer if you think about what the type checking algorithm is doing. First, Flow checks that the type annotation of your getAddFn is correct. The function returns number => number => number in one place, and (number, number) => number) in another. Flow thus infers the the type (number => number => number) | ((number, number) => number) (number => number => number) | ((number, number) => number) for the function.

Then, Flow holds onto this type and completely forgets about the function body. This is what I mean by the distinction above; once Flow knows that a function returns a union type, it knows everything it wants to about that function, and won't revisit it. Given this and the first bullet point above, the errors hopefully start to make sense.


As an aside, hopefully one day this sort of thing will be possible if Flow ever gets proper support for intersection-typed functions. Intersection types are documented here , but there are open github issues on the fact that they don't work with functions, such as this one here . If they worked properly, then it might be possible to use them alongside Flow's literal types to implement something like this. The trick would be to overload the function's type to say that it is both a function from the literal value true to something of type number => number => number and a function from the literal value false to something of type (number, number) => number . Alas, we currently don't have the ability to do this, and it is unclear whether we ever will.

It seems flow doesn't like that your signature appears to be

num -> num | num -> num -> num

This means the exact type would have to be checked at runtime.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM