我想将以下逻辑封装到辅助函数中:
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
。 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
但似乎不值得。