输入捕获的拒绝承诺 return 值

Typing a caught rejected promise return value

我希望这段代码能够编译:

function promiseCatchType(): Promise<[boolean, null]>
function promiseCatchType(): Promise<[null, string]> {
  return new Promise((resolve, reject) => {
    if (Math.random() > .5) {
      return resolve();
    } else {
      return reject();
    }
  })
    .then(() => [true, null])
    .catch(() => [null, 'error']);
}

但它抛出我那个错误:

Type 'Promise<boolean[] | [any, string]>' is not assignable to type 'Promise<[null, string]>'.
  Type 'boolean[] | [any, string]' is not assignable to type '[null, string]'.
    Type 'boolean[]' is missing the following properties from type '[null, string]': 0, 1

在这里,我声明我的函数可以return两种不同类型的元组,一种在成功的情况下,另一种在不成功的情况下。

我想这样处理我的错误:

const [success, error] = await promiseCatchType();
if (error !== null) {
  // error is a string
  return 'An error occurred:' + error;
}

// Do something with `success`, if error is not null, then data must be of `boolean` type

有什么办法可以实现吗?

您必须进行两项更改,其中一项我理解,另一项我坦率地说不理解。 :-)

  1. 您需要声明两个外部签名,然后声明一个支持它们的实现签名。现在您只有一个外部签名和一个不兼容的实现签名。

  2. 你必须向 TypeScript 保证 [true, null][boolean, null]。我不明白为什么。在将 [true, null] 推断为 (boolean|null)[] 而不是 [boolean, null],即使它推断 [null, 'error'][null, string],而不是 (null|string)[]

所以:

function promiseCatchType(): Promise<[boolean, null]>;                 // 1
function promiseCatchType(): Promise<[null, string]>;                  // 1
function promiseCatchType(): Promise<[boolean, null]|[null, string]> { // 1
  return new Promise((resolve, reject) => {
    if (Math.random() > .5) {
      return resolve();
    } else {
      return reject();
    }
  })
    .then(() => [true, null] as [boolean, null])
//                          ^^^^^^^^^^^^^^^^^^^−−−−−−−−−−−−−−−−−−−−−−−−−− 2
    .catch(() => [null, 'error']);
}

Playground link

这里有几个问题:

  • 您将 return 值表示为 overloads... 但是对于重载,实现的签名不算作调用签名之一。因此,您公开的唯一调用签名是 function promiseCatchType(): Promise<[boolean, null]>。如果您想公开两个调用签名,您可以编写 function promiseCatchType(): Promise<[boolean, null]>; function promiseCatchType(): Promise<[null, string]>;,然后编写一个 return 类似于 Promise<[null, string] | [boolean, null]> 的通用实现。但是:

  • 仅 return 类型不同的重载通常没有用。编译器无法判断哪个调用签名 promiseCatchType() 应该匹配,实际上你在调用它时总是会得到第一个。相反,我会忘记重载并拥有一个 return 是 union.

    的签名
  • [true, null] 这样的数组文字往往被推断为像 (boolean | null)[] 这样的数组,而不是元组。有不同的解决方法,但一种是明确断言 [true, null] as [boolean, null].

这使我得到以下代码:

function promiseCatchType(): Promise<[boolean, null] | [null, string]> {
  return new Promise((resolve, reject) => {
    if (Math.random() > .5) {
      return resolve();
    } else {
      return reject();
    }
  })
    .then(() => [true, null] as [boolean, null])
    .catch(() => [null, 'error'] as [null, string]);
}

调用的时候又出现了一个问题。将数组解构为单独的 successerror 将使编译器无法跟踪它们之间的相关性。编译器通常无法将联合类型值视为相互关联。有关详细信息,请参阅 microsoft/TypeScript#30581。如果您检查 error === null,编译器将无法理解这会使 success 成为 boolean 而不是 null.

async function fooBad() {
  const [success, error] = await promiseCatchType();
  if (error !== null) {
    return 'An error occurred:' + error;
  } else {
    success; // boolean | null ?!
  }
  return;
}

相反,您可以将未解构的元组视为 discriminated union,并且仅在测试后取出其成员。这行得通,但可能不像您想要的那样地道:

async function foo() {
  const ret = await promiseCatchType();
  if (ret[1] !== null) {
    const error = ret[1];
    return 'An error occurred:' + error;
  } else {
    const success = ret[0]; // boolean
  }
  return;
}

事实上,我倾向于从元组重构到更明显的可区分联合,如 {success: true, value: boolean} | {success: false, errorMessage: string},而不是完全不解构。但这开始离问题越来越远,所以我就到此为止。


希望对您有所帮助;祝你好运!

Playground link to code