当我从 await 移动到 Promise.all 时,TypeScript 函数 return 类型错误

TypeScript function return type error when I move from await to Promise.all

我有一个功能。 return 类型是用户的承诺:

async function doThing(): Promise<User> {
  const one = await getOne();
  const two = await getTwo();
  return {
    one,
    two
  }
}

我正在更改函数以使用 Promise.all:

async function doThing(): Promise<User> {
  const onePromise = getOne();
  const twoPromise = getTwo();

  await Promise.all([onePromise, twoPromise])
    .then(([one, two])=>{
      return {
        one,
        two
      }
    });
}

但是现在我收到一个 TypeScript 错误:

TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.

令我惊讶的是我在代码的第二个版本而非第一个版本中遇到错误。我尝试捕获错误,但没有任何区别:

  await Promise.all([onePromise, twoPromise])
    .then(([one, two])=>{
      return {
        one,
        two
      }
    }).catch(error => {
       return Error(error);
    });

但是在 Promise.all 之后抛出错误确实会使错误消失:

  await Promise.all([onePromise, twoPromise])
    .then(([one, two])=>{
      return {
        one,
        two
      }
    });
    throw new Error("oh no!")

如@jcalz 所述,您的代码中缺少 return 语句。简短的回答是 .then return 中的 return 语句仅适用于您在 .then 中定义的函数,而不适用于更大的 doThing 函数。

awaitPromise.allPromise.then 都在起作用的情况下,这有点令人困惑,因此我们可以对其进行深入分析。它帮助我隔离方法链中的每个项目并查看它们实际上是什么 return.

但首先,让我们回顾一下这些东西的作用:

  • Promise.all 将承诺列表 (Promise<any>[]) 组合为列表承诺 (Promise<any[]>)。这很方便,允许我们只等待一个承诺来获得我们所有的价值。

  • Promise.then 允许我们在 Promise 完成后对其进行操作(类似于等待),在这种情况下,将 Promise 转换为不同的类型。这是通过定义一个函数来完成的,该函数接受输入值和 returns 一种新类型的值。请务必注意,该函数内部的 return 语句确定 .then 的 return 值,而不是调用它的函数。

  • await:Await 从 Promise 中解包一个值。它还会在等待时阻止函数的执行。

所以让我们打破这个链条并添加显式类型来跟随它:

async function doThing(): Promise<User> {

  // Promises are created for whatever type getOne and getTwo return, let's say it's any
  const onePromise: Promise<any> = getOne();
  const twoPromise: Promise<any> = getTwo();

  // Promise.all converts a list of Promises (Promise<any>[]) in to a promise of a list (Promise<any[]>)
  const allPromises: Promise<any[]> = Promise.all([onePromise, twoPromise]);

  // .then convers the Promise of a list (Promise<any[]>) in to a promise of a user (Promise<User>)
  // The function inside takes in the unwrapped any[] and returns whatever we want wrapped in a new promise (User, in this case)
  // Note that the return here is determining the return value for `.then`, NOT for `doThing` because it is nested inside a separate function.
  // `return` statements are subject to scoping rules similar to variable scope
  const userPromise: Promise<User> = allPromises.then((values: any[]) => return { values[0], values[1] });

  // await unwraps the promised user (Promise<User>) to just a User
  const user: User = await userPromise;

  // We can now return, this is the line that was missing from your original code.
  return user;
}

所以你的 await Promise.all(...).then(...) 链会产生一个 User 对象...但是如果没有 return 或赋值给一个变量,你的函数就不会对它做任何事情。您的代码只需要将 return 添加到该链的前面。

请注意,在我看来,这可以更简单地完成,无需调用 .then 而是等待 Promise.all

async function doThing(): Promise<User> {
  const onePromise: Promise<any> = getOne();
  const twoPromise: Promise<any> = getTwo();

  // Promise.all returns Promise<any[]> and awaiting returns any[]
  const values: any[] = await Promise.all([onePromise, twoPromise]);

  // Now we can return directly from here
  return { values[0], values[1] };
}

另外,为什么抛出一个新错误会停止 TypeScript 错误? TypeScript 错误是由 TypeScript 编译器检测到一个方法会在没有 returning 值的情况下执行到最后时生成的。通过在函数中抛出一个错误,你确保解释器 永远不会 到达函数的末尾而不需要 return... 因为它永远不会到达函数的末尾功能。所以代码是“正确的”,即使它不起作用。