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

Promise / Async / Await Inconsistencies / Issue #5294

Closed
bradennapier opened this issue Nov 8, 2017 · 39 comments
Closed

Promise / Async / Await Inconsistencies / Issue #5294

bradennapier opened this issue Nov 8, 2017 · 39 comments

Comments

@bradennapier
Copy link

bradennapier commented Nov 8, 2017

Ok so this one took me forever to simplify to the point we can see what is happening here, although it is still a pretty confusing bug which is already extremely nasty. It only shows itself in very specific situations and causes confusing issues which are often masked as different bugs completely!

This issue is definitely avoidable - but the end result shows that there is a need to handle this situation in a better way.


The Problem

When using async/await there are times that it ends up returning the Promise class as the return type. Meaning that in order to use the return value in this case as an object of any form, you need to also refine !val instanceof Promise.

Many times it doesn't actually show that this is happening. I haven't been able to refine the specific cases I have run into but it ends up happening when this error compounds onto others and Flow decides to only show the errors that this causes rather than the underlying issue itself.

Note: This issue also will ONLY occur when importing. Any repro attempt using the Try playground is going to work fine from what I can tell.


Overview

Essentially the end result looks like this (note the examples here are all over simplified)

We are programming and all is well, we inspect return types and we like what we are seeing:

image

Then all of the sudden things get problematic. We start seeing errors about Promises and unions:

Promise
  This type is incompatible with the expected param type of
  union: type application of class `Promise` | type parameter `T` of await
  ---
  Member 1:
    type application of class `Promise`
    ---
      Error:
      object type
      ---
        This type is incompatible with
        object type
        ---
        Property `foo` is incompatible:
  Member 2:
    type parameter `T` of await
    ---
      Error:
      property `foo`
      ---
      Property not found in
      Promise
      ---

Upon inspection, if we are lucky enough that this is the error that actually shows, we can see that the following has happened:

image


The Analysis

It is likely due to the following logic within the definition of $await which is also mirrored through within Promise (although it doesn't appear to occur wh

declare function $await<T>(p: Promise<T> | T): T;

This kind of logic is used throughout and the problem seems to come in when refinement causes it to return the second value within the union rather than the first (and the result is Class<Promise>)


Re-Creation

So the issue seems to occur when a few parameters are met:

  1. We call a function that is from another function that will return a Promise.
  2. We have some kind of annotation indicating an object.
  3. We assign the value to something (this, another object, etc) with an annotation.
/* @flow */
import test from './test';

type Blah = {
  foo: string,
};

const obj: { val?: Blah } = {};

async function start() {
  const val = await test();
  if (val != null && typeof val === 'object' && typeof val.foo === 'string') {
    obj.val = val;
  }
}

then from ./test.js , return any kind of promise.

/* @flow */

export default function foo(): Promise<{}> {
  return Promise.resolve({
    foo: 'bar',
  });
}

image


Closing

While the reason for this becomes obvious with this simple example, the issue ends up showing itself in extremely nasty and hard to debug situations.

In this case it is because the annotation returning the promise clearly only shows {} and doesnt perfectly match the given object.

However, this behavior only occurs when all of the given conditions are met and creates inconsistent handling. Using .then() will never show this bug (that I have experienced) so every time that it has occurred it has ended up been very very confusing.

It took awhile to track down these specifics.

So while it isn't really a bug, I have seen quite a few examples of people reporting issues that are likely related to this specific issue.

It should probably either be made to be much more consistent or to provide an error which is easier to understand what and why the issue is happening.

In my case it was happening when trying to type objects which will be highly variable and trying to maintain coverage as much as possible.

@bradennapier bradennapier changed the title Promise / Async / Await Bug w/ Import & Refinement Promise / Async / Await Inconsistencies / Issue Nov 8, 2017
@aikar
Copy link

aikar commented Nov 11, 2017

I think this relates to your return being Promise<{}> when it should be Promise<{[key: string]: any}>

@bradennapier
Copy link
Author

Nope, in my original implementation I use a variant of that. Also, using any = lost coverage. Our projects have a fairly strict 100% coverage rule minus a few edge cases.

It simply has to do with union handling and refinement issues. It mostly makes sense what it is doing and why, but it is a problem in terms of user confusion and how it is reported. Promises, being asynchronous, tend to introduce a lot of complexity to such things anyway.

@bradennapier
Copy link
Author

bradennapier commented Nov 15, 2017

Running into this issue more and more as I build things up. It appears that if you return ANY object through a Promise w/ async/await it will also return Class<Promise>, breaking code.

For example, I have a class which directly returns:

send = async (...args: Array<any>): Promise<PubChan$EmitResponseRef> => { ... }

When I try to use it:

async function collectResults() {
  const ref = await chan.emit('bar', 'foo').send();
  const { results } = ref;
  if (Array.isArray(results)) {
    for (const result of results) {
      console.log('Result: ', result);
    }
  }
}

It will cause errors.

image

Which appear to be the same issue, it is returning Class<Promise> as a possibility.

image

Whereas if I do not use await, no errors:

image

It can be fixed in this situation by simply changing $await to the following:

// we use this signature when typing await expressions
declare function $await<T>(p: Promise<T>): T;

However, I can only imagine this is going to break things in many other situations.

Also notable is the fact it appears to lose it's exactness when using await

@painedpineapple
Copy link

@bradennapier I believe i'm experiencing the same thing, not sure if the examples here help, https://stackoverflow.com/questions/47817511/flow-errors-on-promises

@jwickens
Copy link

I'm surprised that this issue has so few comments, the patchy support for await/async is my number one frustration with flow.

@aikar
Copy link

aikar commented Jan 16, 2018

Cant say I've ran into the issues described here, and I'm using Promises everywhere in my code.

There must be something specific being done to trigger the supposed issue, or there is a misunderstanding either one.

Flow's typedef is correct, see in console:

(async function () { console.log(await 4); })();
// prints 4

My only frustration I run into is when I do something wrong and the error message isn't as clear as to what my error is, or where the error sourced.

IE i once had const foo = await bar; when i needed const [foo, err] = await bar;

then when I used foo.something - it triggered errors in other confusing locations. Flow knew something was wrong, but didn't tell me exactly why.

But ive yet to write valid code working with promises that flow complained about.

@rt2zz
Copy link

rt2zz commented Feb 14, 2018

I am getting the same behavior, when awaiting a method which returns a promise, the resultant type is ExpectedType | Promise

Moreover applying @bradennapier definition of await does fix the return type to no longer include Promise, but it does as he points out break other aspects of the typing.

For now we are working around this by applying the type explicitly at the callsite, i.e.

let foo = await getFoo()
// foo: ExpectedType | Promise

let foo: ExpectedType = await getFoo()
// foo: ExpectedType

@rakelley
Copy link

This is still completely broken, even the most basic tryflow example doesn't work as of 0.79.1, including attempts with @rt2zz 's workaround or by wrapping the promise locally.

@rakelley
Copy link

Okay, I didn't expect to have to explicitly type the implicit wrapper, but regardless that just shifts the problem one call level up (let somevar = await testFuncA(); is still going to be Promise | Foo instead of Foo).

@aikar
Copy link

aikar commented Aug 29, 2018

Looking back at the original complaint, i think this whole thing is without merit.

If your method is marked async, it returns a promise.... no ifs ands or buts.

Try this in the REPL:

(async function() {})()

You will get a Promise.

If you are expecting to write async function getFoo(): Foo {}, your expectations are wrong.

async function getFoo(): Promise<Foo> {
    return new Foo();
}
const foo: Promise<Foo> = getFoo();
const foo: Foo = await getFoo();

That is the only valid code for async functions and it works just fine in flow.

This is where it gets dirty:

let cached: Foo;
function getFoo(): Promise<Foo> | Foo {
    if (cached) {
        return cached;
    } else {
        // yay dirty race condition bug here too!
        return new Promise((resolve, reject) => {
            fooProvider().then((foo) -> {
                cached = foo;
                resolve(foo);
            }).catch(reject);
        });
    }
}

const maybeFoo: Promise<Foo> | Foo = getFoo();
// We MIGHT of been cached, maybe not? let's guarantee we have a foo:
const foo: Foo = await Promise.resolve(maybeFoo);

You now have a function with mixed return types. You really should avoid this.... just mark the getFoo method async and now your function consistently returns Promise

@rakelley with async keyword, you will never ever have Promise | Foo, only if you do this 2nd example where you don't use the async function keyword.

@aikar
Copy link

aikar commented Aug 29, 2018

I take back part of my last statement. I see the OP is mentioning something else, but I can't say I've ran into that scenario.

But as I said in January, Flow's typedef is correct.

I tried the OP's suggested broken code and fixed the return type of the test/foo method and it passes without error:
https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoApgDwA5wE4AuYAJulAIYCuMRUlAdgMYECWc9YUccAFAJQAuMAAU8cALYsAzugA8AbwDaAa3QBPIVIJ4W9AOYBdIeXpqAvgD4w81GDB50BSng6iJ09ADoHUuDABu6Dw2dnZccEIA5ABG5HiRADS2YGZ8ANyoZqioBGrY6GAAQjDkABZgALzWyeGa2rp6SWYZqIzsWmBw0QBWQvJg-uQwAPxCxWUpldbN2eRSakycDMxsHFpxBPzVdm30HYMwU+QI5Cy03PwZdixQYDwHYACEVfTUhwBk72C5+XC3DxVAWBIl1uuhmJEwJ9vnl0H8BkNPOFKkDIlodPpInxtqFQZ4AQiYFcUpkgA

@stevepeart
Copy link

stevepeart commented Nov 12, 2018

Anyone getting here through google, the solution for me was that i needed to define the type of return in the function i was await-ing:

async someFunction(someVar: string): Promise<Object> {}

Given that the method was to return an Object from the promise. This took away the errors for me.

@bradennapier
Copy link
Author

bradennapier commented Nov 30, 2018

FYI Still running into this on a completely separate situation. Again its that await returns a possible promise which is impossible since it will resolve to a value not a promise 100% of the time.

image

image

@mgrip
Copy link

mgrip commented Dec 5, 2018

Running into this situation as well using node-pg.

query.js

// @flow strict
import { Pool } from "pg";
import type { ResultSet } from "pg";

const pool = new Pool();

export default (text: string, params: Array<any>): Promise<ResultSet> =>
  pool.query(text, params);

test.js

// @flow strict
import query from "./query";

export async function runQuery() {
  const test = await query("SELECT * FROM TABLE;", []);
  return test;
}

Flow seems to think test is of type Promise<ResultSet> | ResultSet, when it should quite obviously be just ResultSet

@bradennapier
Copy link
Author

bradennapier commented Dec 5, 2018

Yep you need to provide the type in this case (on the result var directly) and it will solve the issue. Not ideal clearly but it has a workaround at least.

@bradennapier
Copy link
Author

Also to those of you mentioning you can’t recreate it in flow try - as I pointed out it ONLY HAPPENS when you import from another file.

In the same file it seems to be fine.

@silvenon
Copy link
Contributor

silvenon commented Dec 11, 2018

I can confirm, I have similar errors only when multiple files are involved.

@henrikland
Copy link

I think I have run into this issue.
I have 2 files:

File1.js:

const getThings = async (): Promise<Array<SomeType>> => { ... };
export { getThings };

File2.js:

import { getThings } from 'File1';

const doStuffWithThings = async (): Promise<Array<SomeOtherType>> => {
  const things: Array<SomeType> = await getThings();
  ...
  things.forEach(thing => { ... });
  ...
}

Flow complains that Either cannot call things.forEach because property forEach is missing in Promise.
Changing the return type of getThings to Promise<any> makes the error go away though.

@nmote
Copy link
Contributor

nmote commented Feb 12, 2019

I think it would be really helpful if somebody put up a gist with a complete example that exhibits the unexpected behavior. Unless I missed something, all I've seen on this issue are snippets from actual codebases, or other partial examples.

I've seen what I think is the same issue during my own use of Flow. It's always been a legitimate error, though, which additional type annotations helped to reveal. If I'm correct, this issue is about opaque error messages, and not about Flow erroring when it should not. A concrete example would help determine whether I'm correct.

@bradennapier
Copy link
Author

bradennapier commented Feb 13, 2019

Thanks @nmote ! I believe you are 100% correct, the issue happens if you have any accesses on the value which are not in the expected return type in the Promise, and I can see exactly why this happens if you look at the definition of $await.

I believe the best solution would be for flow to understand that a call using await can never result in the Promise<> part of the union which should then simultaneously fix the opaque error messaging.

In all my situations so far it does end up being an error on my part - although sometimes very hard to find as it can be a random property access at nearly any point after that since flow is generally fairly smart at these things.

Another key point is it only appears to happen if you are importing the async function from another file.

@aikar
Copy link

aikar commented Feb 13, 2019

The typedef signature is correct and does not need to be changed.
The problem is that in same file the await signature of Promise<T> | T maps the input to Promise<T> (which gives T the correct value) and other-file usage maps to both, giving both as a potential response.

@bradennapier I state this in hopes that it makes what the true nature of this bug is, to be more clear, as you tacked on the note about 'another file' as 'another' key note, but that is the only key note that matters here.

I believe the best solution would be for flow to understand that a call using await can never result in the Promise<> part of the union which should then simultaneously fix the opaque error messaging.
This is resolved by ensuring that the input maps to Promise<T> and not the | T portion, as it currently does within the same file.

My assumption is that it the passed argument to the parameter is able to 'accurately' identify it matches the Promise signature and reduces to only that as a match, but the assurance of cross-file type validation is treated differently and doesn't reduce to only one.

@nmote does that help? Sorry I don't have time to make a gist atm though.

@akrieger
Copy link

@nmote I think you're right that this about 'opaque error messaging'. In all my attempts to make a small repro case, flow check --show-all-branches correctly identifies the actual location of the problem (in one of the extra shown branches), but the primary error is always attributed to 'cannot call await because this or that type is incompatible with Promise'.

See https://gist.github.com/akrieger/f1aa74fd5b5819cef96c100fc034d63b for an example. I've commented in index.js the place where the error is introduced and how to resolve it, and described how that changes flow's behavior.

@kweiberth
Copy link

kweiberth commented Mar 15, 2019

@akrieger we had a similar issue with this "red herring" error message, and flow check --show-all-branches helped us. Thank you 🙏

Our example minimally was this:

// file1.js

type Response = {
    propA?: boolean,
    propB?: string,
}

export async function anAsyncFunction(input: any): Promise<Response> {
    // stuff
}
// file2.js

import { anAsyncFunction } from './file1.js'

async function doSomeWork() {
    ...
    const { propA = false, propB } = await anAsyncFunction('some input')
    ...
    const templateLiteral = `here's a string with ${propB} interpolated`
    ...
}

When running flow, the error we saw was:

Cannot call await with anAsyncFunction(...) bound to p because property propA is missing in Promise [1].

However, after running flow check --show-all-branches, we then saw this error plus the real error:

Cannot call await with anAsyncFunction(...) bound to p because:
 • Either cannot coerce propB to string because undefined [1] should not be coerced.
 • Or property propB is missing in Promise [2].

The fix was to either add a sensible default when destructuring the response, like:

const { propA = false, propB = '' } = await anAsyncFunction('some input')

or use the String() global to force string in the template literal:

const templateLiteral = `here's a string with ${String(propB)} interpolated`

I think this is a bug bc propA was in fact defined in the Promise's return value. Why did flow "choose" the wrong error?

@silvenon
Copy link
Contributor

@kweiberth propB is specified as an optional property, so it can be either a string or missing, i.e. undefined. You shouldn't implicitly coerce undefined to a string because that strongly suggest that you aren't aware of its type and that you're making a mistake. The error goes away if you cast it into a string with String because then you're being explicit about your intentions. It also goes away when you default to an empty string, because then it can't be undefined, therefore it can be interpolated.

@kweiberth
Copy link

Hi @silvenon that makes a lot of sense. I actually mistyped on the last line and I've since updated it. The issue I see here is that the error message that flow originally gave was that propA type declaration was missing, when it wasn't. The real error it should've been showing was the propB undefined/coercion error that you described, which was displayed to me only after running flow check --show-all-branches

@cappslock
Copy link

I can confirm that declaring the expected return type on the variable seems to clear it up.

i.e. do this:

const thing: Thing = await getThing()

not this:

const thing = await getThing()

@silvenon
Copy link
Contributor

silvenon commented Apr 9, 2019

@cappslock hm, that doesn't seem right, are you sure that you didn't lose the type for getThing or getThing() somewhere, so it ended up being any? If you do const thing = await getThing(), what is the type of thing?

@wi-ski
Copy link

wi-ski commented Jul 15, 2019

This is a silly issue to have :)

@goodmind
Copy link
Contributor

@wi-ski this can't be worse than this one #2093

@goodmind
Copy link
Contributor

goodmind commented Jul 15, 2019

This seems easy to fix, just replace $await definition with this

declare function $await<T: Promise<any>>(p: Promise<T>): $Call<typeof $await, T>;
declare function $await<T>(p: Promise<T>): T;
declare function $await<T>(p: T): T;

This is also just better definition overall, because it makes Promise<Promise<Promise<Promise<number>>>> to number

@goodmind
Copy link
Contributor

Well, with mine definition, Flow tests on await just hangs :\

@goodmind
Copy link
Contributor

Also it breaks another test, well that's too bad

@gaastonsr
Copy link

Thank you @kweiberth! the issue was indeed that a type was being coerced a few lines after... still the error message is super confusing.

@terite
Copy link

terite commented Mar 12, 2020

I was able to simplify the the reproduction to a single index.js file, plus a standard config, on the latest version of flow. https://gist.github.com/terite/47e5d725bae07723fb11ea5b4a470a65

I've tried to demonstrate that this error

  • does not depend on the imported item being an async function, or even a promise.
  • Is not the result types getting lost and becoming any.
  • is not about opaque error messaging, as some issues are not caught (though it definitely can cause opaque errors)

@aikar
Copy link

aikar commented Mar 12, 2020

ignore my last reply, i re-interpreted it the report.

ultimately its because you forced flow to pick promise return BECAUSE the left hand side is wrong

Only way i can see fixing this to identify the users error is to explicitly look for explicit Promise Left Hand Side types of an await.

@terite
Copy link

terite commented Mar 12, 2020

await is one common way to trigger this bug, but the fact that imports are a necessary step in reproduction hints that something more interesting is going on.

I've added two.js to my gist which demonstrates a variation of this bug which is not dependent on async/await/promises. I believe something similar is probably possible using $Call.

https://gist.github.com/terite/47e5d725bae07723fb11ea5b4a470a65#file-two-js

@gnprice
Copy link
Contributor

gnprice commented Dec 27, 2020

This appears to have been fixed!

When I run a flow built from a recent version from master (f1a694e) on @terite's helpful repro, the error messages are in all the right places and none of the wrong ones:

Full error output
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ index.js:11:23

Cannot assign await fs.promises.stat(...) to b because Stats [1] is incompatible
with number [2]. [incompatible-type]

     index.js
        8│     const a: Stats = await fs.promises.stat('/')
        9│
       10│     // good: flow forbids specifying an incorrect type
 [2]   11│     const b: number = await fs.promises.stat('/')
       12│
       13│     // BUG: missing error here, await will not return a promise
       14│     const c: Promise<Stats> = await fs.promises.stat('/')

     /tmp/flow/flowlib_502b8788d4ba690b_1000/node.js
 [1] 1331│     stat(path: FSPromisePath): Promise<Stats>,


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ index.js:14:31

Cannot assign await fs.promises.stat(...) to c because Stats [1] is incompatible
with Promise [2]. [incompatible-type]

     index.js
       11│     const b: number = await fs.promises.stat('/')
       12│
       13│     // BUG: missing error here, await will not return a promise
 [2]   14│     const c: Promise<Stats> = await fs.promises.stat('/')
       15│ }
       16│
       17│ async function testAwaitNonPromise() {

     /tmp/flow/flowlib_502b8788d4ba690b_1000/node.js
 [1] 1331│     stat(path: FSPromisePath): Promise<Stats>,


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ index.js:23:7

Cannot get b.then because property then is missing in Number [1]. [prop-missing]

 [1]  4│ const F_OK: number = fs.F_OK
       :
     20│
     21│     // good: flow knows awaiting non-promises does not make them promises
     22│     const b = await F_OK
     23│     b.then // error is here, in the correct spot
     24│
     25│     // good: flow does not get confused if the awaited value is external, but not imported
     26│     const c = await Uint8Array.length


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ index.js:27:7

Cannot get c.then because property then is missing in Number [1]. [prop-missing]

     index.js
      24│
      25│     // good: flow does not get confused if the awaited value is external, but not imported
      26│     const c = await Uint8Array.length
      27│     c.then // error is here, in the correct spot
      28│
      29│     // BUG: flow gets confused if the awaited value is imported
      30│     const d = await fs.F_OK // error is here, the wrong spot

     /tmp/flow/flowlib_502b8788d4ba690b_1000/core.js
 [1] 347│     length: number;


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ index.js:31:7

Cannot get d.then because property then is missing in Number [1]. [prop-missing]

     index.js
       28│
       29│     // BUG: flow gets confused if the awaited value is imported
       30│     const d = await fs.F_OK // error is here, the wrong spot
       31│     d.then
       32│ }
       33│

     /tmp/flow/flowlib_502b8788d4ba690b_1000/node.js
 [1] 1201│   declare var F_OK: number;


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ two.js:13:7

Cannot get a.length because property length is missing in Number [1]. [prop-missing]

 [1] 10│     const a = assertNotNull(5)
     11│
     12│     // good, flow knows number has no length
     13│     a.length
     14│ }
     15│
     16│ function testImportedValue() {


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ two.js:19:7

Cannot get a.length because property length is missing in Number [1]. [prop-missing]

     two.js
       16│ function testImportedValue() {
       17│     // BUG: flow gets confused if the provided value is imported
       18│     const a = assertNotNull(fs.F_OK) // error is here, the wrong spot
       19│     a.length
       20│ }
       21│

     /tmp/flow/flowlib_502b8788d4ba690b_1000/node.js
 [1] 1201│   declare var F_OK: number;



Found 7 errors

OTOH the issue reproes if I use v0.141.0, the latest release.

In fact, that commit and v0.141.0 have just a very few differences:
$ git log --oneline --graph --boundary v0.141.0...f1a694ef1 --cherry-mark
= d8b5711 (HEAD, tag: v0.141.0, upstream/v0.141) Flow v0.141.0
= 1a6b465 cache resolved repositioned OpenTs
= 40b9ba2 fix flow error in packages/flow-dev-tools
= ece39a1 add Find_documentation.def_loc_to_comment_loc_map
= b1fdafe don't insert redundant = in JSX autocomplete when attribute has value
= 0c4eae8 [easy] switch Autocomplete_js.Acjsx to record argument
* cc9779c Back out "[Flow] Refactor Merge_js"
* bd87898 Back out "[flow] merge imports before check"
* 9ee4de4 Back out "[Flow] skip post_merge_checks in types-first merge"
| = f1a694e (master) Flow v0.141.0
| = 7970cec cache resolved repositioned OpenTs
| = 3a33b21 add Find_documentation.def_loc_to_comment_loc_map
| = 033a851 fix flow error in packages/flow-dev-tools
| * 8efa4d5 move tests to "classic-only"
| * 28f5aa8 upgrade tests/{react*,relay} to types-first
| * 362ee0e upgrade tests/o* to types-first
| * 5834f34 upgrade tests/more_* to types-first
| * 0ff0dbf upgrade tests/lint_cli_* to types-first
| * b746999 upgrade tests/{fixpoint,demo,implicit_instantiation,type_visitor} to types-first
| = 923d58e don't insert redundant = in JSX autocomplete when attribute has value
| = 27142d1 [easy] switch Autocomplete_js.Acjsx to record argument
|/
o 38d2dcd [easy] remove debug print statement from ty_members

which, other than the test suite, are entirely that v0.141.0 has reverted some changes that look related to "types-first".

So it appears that those changes (which were reverted just for that release) fix this issue. (Or perhaps more accurately: that types-first fixes this issue, but reverting those changes meant types-first doesn't apply here.) Hopefully that means that a future release will include those changes, and will fix this bug.

@gkz
Copy link
Member

gkz commented Sep 2, 2021

Checked example from @terite with Flow 0.159 and looks like this is now fixed.

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

No branches or pull requests