diff --git a/packages/compiler/src/frontend/lowering/lower-calls.ts b/packages/compiler/src/frontend/lowering/lower-calls.ts index 6229917a2..45d470c60 100644 --- a/packages/compiler/src/frontend/lowering/lower-calls.ts +++ b/packages/compiler/src/frontend/lowering/lower-calls.ts @@ -6908,7 +6908,10 @@ export function lowerPromiseMethodCall( if (ts.isBlock(hb)) { catchBody = lowerer.lowerStmts(hb.statements.nodes); } else if (R.kind === "void") { - catchBody = [{ kind: "exprStmt", expr: lowerer.lowerExpr(hb), loc: locOf(hb) }]; + // The handler's value is discarded, so the body lowers as a + // statement: `() => undefined` is a pure no-op (a bare unit + // literal has no IR value), not an expression to evaluate. + catchBody = [lowerer.lowerExprStatement(hb)]; } else { // Bare-expression handler: `(e) => v` (promise results flatten // through the async-return path, like any `return v`). diff --git a/tests/corpus/promise-void-catch-undefined.ts b/tests/corpus/promise-void-catch-undefined.ts new file mode 100644 index 000000000..ee7285d8a --- /dev/null +++ b/tests/corpus/promise-void-catch-undefined.ts @@ -0,0 +1,31 @@ +// `Promise.catch(() => undefined)` — the handler's value is +// discarded and the recovered promise settles as void. The handler runs +// exactly once, and the chain continues on the next turn whether the +// receiver fulfilled or rejected. + +const ok: Promise = Promise.resolve(); +const bad: Promise = Promise.reject(new Error("boom")); + +ok.catch(() => undefined).then(() => console.log("fulfilled recovered")); + +bad + .catch(() => { + console.log("rejected recovered"); + }) + .then(() => console.log("rejected continued")); + +let runs = 0; + +function recover(): Promise { + runs++; + return runs % 2 === 1 ? Promise.reject(new Error("odd")) : Promise.resolve(); +} + +async function run(): Promise { + await recover().catch(() => undefined); + console.log("awaited recover", runs); + await recover().catch(() => undefined); + console.log("awaited recover", runs); +} + +void run().then(() => console.log("done"));