TypeScript 值断言可以异步吗?

问题描述 投票:0回答:1

我想将以下逻辑封装到辅助函数中:

if (!response.ok) {
  throw Error(await response.text());
}

这样它就可以读作:

await myAssert(response.ok, async () => await response.text());

但我无法找出正确的 TypeScript 语法。如果我这样做:

async function myAssert(
  precondition: boolean,
  messageMaker: () => Promise<string | Error>
): asserts precondition
{
  const message = precondition ? "-unused-" : await messageMaker();
  assert(precondition, message);
}

然后 TypeScript 编译器抱怨:

异步函数或方法的返回类型必须是全局 Promise 类型。

但是如果我这样做:

async function myAssert(
  precondition: boolean,
  messageMaker: () => Promise<string | Error>
): Promise<void>
{
  const message = precondition ? "-unused-" : await messageMaker();
  assert(precondition, message);
}

然后在调用代码中,TypeScript 编译器不明白在等待断言后

response.ok
必须始终为 true。

有没有办法可以在 TypeScript 中同时拥有两者?

typescript async-await assertion
1个回答
0
投票

目前在 TypeScript 中不可能将 断言函数 设为

async
microsoft/TypeScript#37681 有对此的开放功能请求,但目前尚不支持。

您能做的最好的事情就是重构,以便您关心的整个状态由

async
函数返回。但如果你想让它变得通用,结果就会变得一团糟。您可能会通过传入自定义类型保护函数并断言其结果来逃脱:

async function myAssert<T, U extends T>(
  state: T & (Exclude<T, U> | U),
  predicate: (t: T) => t is U,
  msgMkr: (t: Exclude<T, U>) => Promise<string | Error>
): Promise<U> {
  if (predicate(state)) {
    return state;
  } else {
    const msg = await msgMkr(state);
    throw (typeof msg === "string") ? new Error(msg) : msg;
  }
}

目前在 TypeScript 5.4 中很痛苦,但在 TypeScript 5.5 中我们可以期待 microsoft/TypeScript#57465 帮助推断类型保护函数。所以我们可以想象采取这样的事情

type Resp =
  { success: true, data: string } |
  { success: false, error(): Promise<string> };

async function foo(r: Resp) {
  if (!r.success) { throw new Error(await r.error()) }
  console.log(r.data.toUpperCase());
}
foo({ success: true, data: "abc" }); // ABC
foo({
  success: false, error() { return Promise.resolve("bad") }
}).catch(e => console.log(e)); // bad

并用

myAssert()
将其包裹起来,如下所示:

async function fooWrapped(
  r: Resp
) {
  const goodR = await myAssert(r, r => r.success, r => r.error());
  //  inferred as type guard -----^^^^^^^^^^^^^^
  console.log(goodR.data.toUpperCase());
}
fooWrapped({ success: true, data: "abc" }); // ABC
fooWrapped({
  success: false, error() { return Promise.resolve("bad") }
}).catch(e => console.log(e)); // bad

但似乎不值得。

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.