Typescript async/await 无法确定正确的返回类型

Posted

技术标签:

【中文标题】Typescript async/await 无法确定正确的返回类型【英文标题】:Typescript async/await cannot determine correct return type 【发布时间】:2020-08-26 20:07:09 【问题描述】:

我在 Typescript 中有以下基于 Promise 的方法:

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);
    );


该方法基本上根据另一个查询的结果将过滤器添加到(猫鼬)聚合中。

这工作得很好,Typescript 很高兴。

问题

如果我尝试将其转换为使用 async/await 模式,Typescript 开始抱怨。这是转换后的方法:

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);


这次我在return ...-line 上收到来自 Typescript 的编译错误,告诉我:

TS2322:类型“any[]”不可分配给类型“Aggregate”

Typescript 似乎无法从 query.match(matcher) 函数推断出正确的返回类型,甚至将其转换为 as Aggregate&lt;any[]&gt; 也无济于事。

如果我在基于 Promise 的方法中从 then(..) 函数中删除泛型类型,我会得到与使用 async/await 语法时大致相同的错误。

我正在使用 Typescript 3.7.2 版

谁能解释为什么会发生这种情况,以及是否有一种解决方法我仍然可以使用 async/await - 而不必将部分代码包装在 Promise 中?我觉得我已经尝试过明确地投射每种类型,但到目前为止没有运气。

更新 1

我已将有问题的代码简化为:

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

虽然这个例子基本上什么都没做,但它仍然会导致编译错误。出于某种原因,async 关键字似乎只是打开了 Aggregate 类型。

将此与以下代码进行比较,类似,但不会导致错误:

declare class SomeGenericClass<T> 

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

所以看起来这个问题在某种程度上特定于mongoose 提供的Aggregate 类型。

Here's a reproduction of the issue

【问题讨论】:

这可能与您使用的是any有关。它完全打破了类型检查链。由于查询本身具有any,因此当您使用query.match 时,它无法评估类型。不过,这只是一个假设。如果您在 TypeScript 操场上为我们提供最少的可重现代码会更好。 我尝试将 any 替换为显式类型,但我却得到了错误 Type 'SomeDocument[]' is not assignable to type 'Aggregate&lt;SomeDocument[]&gt;'。稍后我将尝试在在线游乐场中重现它。 在 Promise-base 代码中,您执行 .then&lt;Aggregate&lt;any[]&gt;&gt;,这实际上是手动类型转换返回类型。因此,当您删除它时,promise-base one 中也会引发错误。 async/await 基本代码中的等价物只是将其转换为as Aggregate&lt;any[]&gt;。如果它引发错误,请尝试as any as Aggregate&lt;any[]&gt; 强制它。 findSomething 是如何定义的?可重复的示例会有所帮助 findSomething 方法似乎与它没有任何关系。我还不能在沙盒环境中重现该问题。 【参考方案1】:

虽然我还没有找到确切的解决方案,但我已经找到了解释。

我查看了mongoose implementation of the Aggregate class,他们已将thencatch 函数添加到Aggregate 类中,使其表现得像一个承诺。这将使 async/await 操作符将其视为一个承诺,因为它们将任何具有 then-function 的对象视为一个承诺。这就是Aggregate-class 从异步方法返回时似乎解包的原因。

对于手头的具体问题,作为一种解决方法,我只是将方法的异步部分移回调用代码。基本上就像我开始修补它之前一样。它并不完美,但它确实有效。

【讨论】:

以上是关于Typescript async/await 无法确定正确的返回类型的主要内容,如果未能解决你的问题,请参考以下文章

Async/Await - Typescript 和 ExpressJs

为啥 Typescript 认为 async/await 返回包装在承诺中的值?

实现一个 async/await (typescript 版)

typescript 使用async / await测试NgRx Facade。

如何将 onload 承诺转换为 Async/Await

Typescript async/await 和 angular $q 服务