Flow: (v0.70) Missing type annotation when using array methods with exported function

Created on 17 Apr 2018  路  28Comments  路  Source: facebook/flow

On flow v0.70, array methods / generic methods seem to be broken. This only happens when the function is exported, and uses an array method. Marking a return type of any[] makes flow pass.

Reproduction here:

// @flow
function noErrors() {
  return [].map(b => b);
}

export function noErrors1() : any[] {
  return [].filter(b => b);
}

export function hasErrors() {
  // Errors out below with "Missing type annotation"
  return [].map(b => b);
}

export function hasErrors2() {
  // Errors out below with "Missing type annotation"
  return [].filter(b => b);
}

Errors:

12:   return [].map(b => b);
             ^ Missing type annotation for `U`.
17:   return [].filter(b => b);
             ^ Missing type annotation for `T`.
error messages

Most helpful comment

@lll000111 The error message doesn't tell anything about missing return type. It says that Missing type annotation for 'U' How can you even know what is U without diving into flow core libdefs?

All 28 comments

Why use any? You can use something like Array<string>.

See the changelog:

New missing annotation errors might be reported.

Not an error, quite the opposite actually (it's quite deliberate).

You export a function — Flow expects you to document the function's return type. Your function has no return type annotation. I mean, the error message even tells you!

And use * instead of any, it's counter-productive and there is no reason to turn off type checking, instead let Flow autodetect it. Even better of course if you specify the actual type. Even if Flow can figure it out, human readers (incl. yourself two weeks later) appreciate the documentation a type annotation provides.

Fixed.

@lll000111 The error message doesn't tell anything about missing return type. It says that Missing type annotation for 'U' How can you even know what is U without diving into flow core libdefs?

@villesau

You say:

The error message doesn't tell anything about missing return type

The error message says

Missing type annotation for U.

I'd say "missing annotation" is quite clear. There is a missing annotation. You look at the function and you see — there is no annotation! You check your parameters and the return type, that's all that can be (and that must be) annotated. There is no ambiguity here. Okay, the "U"... so what? You don't need it. All you need is "Missing annotation". Ignore generic components. While it could be improved of course, I think a type system requires a bit of prerequisite knowledge, this isn't aimed at average users. This tool sure has more than enough unsolved issues (that nobody on the owner side even acknowledges, they seem to ignore this entire public "issues" section as far as I can tell), in comparison I'd put this error message quality issue at the bottom of the list.

@lll000111 It asks you to annotate your U, something related to your map or filter, it doesn't say anything return type annotation related. That's the point. Flow rarely requires you to annotate your return type which doesn't make it obvious. When I tried to update to latest Flow, I couldn't even see the benefit of having to explicitly type it: Previously Flow was able to infer the type just fine. At least all the cases I tested. But that's maybe not strictly related to this issue.

@villesau The key is the return statement. "Missing annotation" is only applicable to function signatures and variables.

In this context. related: https://github.com/facebook/flow/issues/2667

Okay okay okay. By all means, let's have a better error message.

This seems like inconsistent behavior. Either all exports should require explicit annotations or non should as long as the implicit types are valid.

I'm pretty sure something seems wrong with exporting anything using an array method.

Given this example https://flow.org/try

// @flow

const someArray: Array<string> = ['hello'];

// Error Here
const a = {
  someValue: someArray.map((x) => x)
  //         ^ Missing type annotation for `U`.
};

// No Errors Here
const b = {
  someValue: someArray
}

export const c = {
  a: a,
  b: b
};

I'm seeing this too. You don't need to wrap the array in an object, as you can see here. Happens in 0.7.0, but not 0.6.9. Adding a type annotation before exporting gets rid of the error

So where do 'U', 'T' or 'S' come from? Are they just randomly generated letters? In that case it would be very helpful to replace them with 'exported function return type' or something like that.

@schumannd this for map, this for filter.

It's _technically_ doing things as it should: you're returning an Array or an Array and it needs a type annotation for the type parameters of the returned array; the problem is it's a little cryptic since the user didn't directly define map or filter, so they wouldn't know what U or T are.

Well yes, flow is throwing an error and thus doing things as it should.
If, however, the error message is not understandable, then there is definitely room for improvement.
Also, what are these letters? Are they the names of the parameters of map and filter? What about 'S'?

I am strongly in favor of doing something about this. Operations on arrays are among the most common things to appear in any code. These cryptic error messages will cause a lot of new users trouble.

I'm not sure it technically _is_ doing the right thing. Return type annotations are not required on all exported methods -- that's easy to verify. So what's special about methods that use array generics?

I don't understand this entirely, but if you do import * as React from "react"; instead of import React from "react"; flow doesn't complain anymore.

@amypellegrini I ended up finding the same conclusion. I still don't completely understand but here is a screenshot that somewhat confirms it

components___flow

https://flow.org/en/docs/react/components/

We already have import * as React from 'react' in our file but it's still giving the error.

@peeyush-ad2games What version of flow are you using?

@functionalStoic we are using 0.78.0

I also ran into the same issue. What's the status of this?

So it's a great discussion, but how do we compile the code like that?

@innokenty the usual setting is with Babel and Webpack, shout out if need more details.

I hope this will help someone out: map is typed as a generic. You want to provide something in the place of U here, as in someArray.map<U>(someMapperFunction) where someMapperFunction is of type T => U.

An example:

dictToOptions = (options: {[string]: string}) => {
  const mapper = (key: string): React.Node => (
    <option key={key} value={options[key]}>{options[key]}</option>
  )
  return Object.keys(options).map<React.Node>(mapper)
}

I'm trying to upgrade my version to 0.91.0 and also having problems with map functions.

Maps like this are causing me the errors:

  renderDescriptions() {
    return this.props.products.map((item, index) => (
      <ol
        key={`description-${index}`}
      >
        {this.renderDescriptionItem(item.descriptions)}
      </ol>
    ));
  }

Changing to this solves the problem:

  renderDescriptions() {
    const descriptions: React.Element<'ol'>[] = this.props.products.map((item, index) => (
      <ol
        key={`description-${index}`}
      >
        {this.renderDescriptionItem(item.descriptions)}
      </ol>
    ));

    return descriptions;
  }

But i'm still curious about something.

There is a Medium post by Flow about this change, in which they state:

Note that builtin generic types like Map, Set, and Promise are not affected. Builtin types are merged early in the analysis, so Flow already complains about missing annotations when their instances reach exports.

So why are we getting errors in built-in map functions, like the one above?

My this.props.products variable is a built-in Array.

@luislhl In your first example...

Flow knows that .map() always returns an array but doesn't know the type of the elements of that array. It could be anything. This information needs to either be explicitly provided like this this.props.products.map<React.Node>( ... or provided in a way that Flow can infer this information.

In your second code block, you specify the type of descriptions as being Array<React.Element>, so Flow can infer that this.props.products.map( ... will return this type.

With an array of ObjectType:

type ObjectType = {
  id: number,
};

using .map<ObjectType>, I got the following error:

property id is missing in React.Element [1] but exists in ObjectType [2] in the return value.

I fixed this error using: .map<{}>

I think if you use $ReadOnlyArray it doesn't require annotation

I ran into this issue today with the following code:

 configuredFlags() {
    return this.props.availableFlags.filter((flag: string) => {
      return this.initialFlagStatus(flag) !== 'default';
    });
  }

The solution was to define a return type for the configuredFlags function:

 configuredFlags(): Array<string> {
    return this.props.availableFlags.filter((flag: string) => {
      return this.initialFlagStatus(flag) !== 'default';
    });
  }

Today I ran on this weird error.

I had this:

type Response = {
  viewer: { paymentRequests: $ReadOnlyArray<PaymentRequest> },
}

export default (companyID: string) => {
  const { data, ...info } = useQuery<Response>(  )
  return {
    ...info,
    data: {
      paymentRequests:  data.viewer.paymentRequests
            .slice()
            .sort(byPayDateDesc)
    },
  }
}

And I was getting this error:

Missing type annotation forT.Tis a type parameter declared in read-only array type [1] and was implicitly instantiated at call of methodslice[2].Flow(InferError)

Luckly I was able to properly "Cast" by doing this:

    data: {
      paymentRequests: data
        ? (data.viewer.paymentRequests
            .slice()
            .sort(byPayDateDesc): PaymentRequest[])
        : data,
    },

But I don't understand why flow was not capable of inferring what was already declared, slice should not change the data in any way, and it doesn't allow you to provide parameters, so this was kind of a dead end until I came to this funny cast.

Was this page helpful?
0 / 5 - 0 ratings