Search code examples
javascriptes6-promiseflowtype

Confusing flow error when dealing with multiple promises and callbacks


I'm adding flow to a project, and I've run into an error I can't figure out. Here's some code that produces the same error:

type user = {
  id: string,
};

const thing = function(): Promise<user> {
  return new Promise(resolve => {
    var p1 = new Promise(innerResolve => {
      getData(data => {
        innerResolve(data);
      });
    });
    Promise.all([p1]).then(result => {
      resolve(result[0]);
    });
  });
};

const getData = function(callback) {
  setTimeout(() => {
    callback({});
  }, 1000);
};

Link to this code on Try Flow, where you can read the error.

In my application, the getData function goes out to a database to get the object. And the type of what it returns in the callback can only be this specific:

{
  [key: string]: string,
}

So it could be called with an empty object, like in the example. If you replace that line with:

callback({id: 'working'})

there are no type errors. But I can't do that in my application.

Another strange thing is that I can remove this whole block:

Promise.all([p1]).then(result => {
  resolve(result[0]);
});

and that also makes it pass the flow test.

And the one thing I'd think would fix this issue -- refinements -- don't resolve the issue.

    if (typeof data === 'object' && typeof data.id === 'string') {
      innerResolve(data);
    } else {
      throw new Error('bad db response');
    }

I haven't been able to figure out any way to get flow to accept this code, without changing something that I can't actually change (like replacing the database code with a simple return).

Can anyone explain the error I'm seeing, and why I'm triggering it?


Solution

  • you are explicitly setting the return type of thing function to Promise<user>, and that's why you get the error since your callback passes the parameter {} which is NOT a user

    I don't know what your specific requirement is or why you are using a timeout but if the return type can be something that is NOT user, meaning any you can make the return type: you can set the return type to Promise<user|any>

    You explicitly set the return type, so you have to return that return type.