Nikolaj Dam Larsen
Nikolaj Dam Larsen

Reputation: 5674

Typescript async/await cannot determine correct return type

I have the following Promise-based method in Typescript:

filterByParams(
  query: Aggregate<any[]>,
  params: ParamsObject
): Promise<Aggregate<any[]>> {
  return this.findSomething(params)
    .then<Aggregate<any[]>>((results: SomeResultType[]) => {
      const matcher = {
        // ... using results to match something
      };

      return query.match(matcher);
    });
}

The method basically adds a filter to a (mongoose) aggregate, based on the result from another query.

This works perfectly fine and Typescript is happy.

Problem

If I try to convert this to use the async/await pattern, Typescript starts to complain. Here is the converted method:

async filterByParams(
  query: Aggregate<any[]>,
  params: ParamsObject
): Promise<Aggregate<any[]>> {
  const results: SomeResultType[] = await this.findSomething(params);

  const matcher = {
    // ... using results to match something
  };

  return query.match(matcher);
}

This time I get a compilation error from Typescript, on the return ...-line, telling me:

TS2322: Type 'any[]' is not assignable to type 'Aggregate< any[]>'

It seems like Typescript somehow cannot infer the correct return type from the query.match(matcher) function, and even casting it to as Aggregate<any[]> doesn't help.

If I remove the generic type from the then(..) function in the Promise-based method, I get largely the same error as when I use the async/await syntax.

I'm using Typescript version 3.7.2

Can anyone explain why this happens and if there's a workaround where I'm still able to use async/await - without having to wrap parts of the code in a promise? I feel like I've tried casting every single type explicitly with no luck so far.

Update 1

I've reduced the problematic code to this:

async filterByParams(
  query: Aggregate<any[]>
) {
  return query;
}

While this example basically does nothing, it still causes the compilation error. For some reason, the async keyword, seems to just unwrap the Aggregate type.

Compare this to the following code, which is similar, but doesn't cause the error:

declare class SomeGenericClass<T> {}

async filterByParams(
  query: SomeGenericClass<any[]>
) {
  return query;
}

So it seems like the issue is somehow specific to the Aggregate type provided by mongoose.

Here's a reproduction of the issue

Upvotes: 4

Views: 611

Answers (1)

Nikolaj Dam Larsen
Nikolaj Dam Larsen

Reputation: 5674

While I havn't found an exact solution, I have found an explanation.

I've looked through the mongoose implementation of the Aggregate class and they've added the then and catch, functions to the Aggregate class to make it act like a promise. This will make the async/await operators treat it like a promise, since they treat any object with a then-function as a promise. This is the reason the Aggregate-class seems to unwrap when returned from an async method.

For the specific problem at hand, as a workaround, I just moved the async part of the method back to the calling code. Basically like it was before I started tinkering with it. It isn't perfect, but it works.

Upvotes: 0

Related Questions