Await each value of for await over an untyped array

A JavaScript array literal that mixes promises and plain values lowers as
a checked-dynamic value, and for await over it took the synchronous
iteration path, binding the promises themselves. It now takes the async
mode of the checked-dynamic loop, which awaits each value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Malte Ubl
2026-10-20 17:37:15 -07:00
co-authored by Claude Opus 5.5
parent 4a90c59cf3
commit ce6be86afb
6 changed files with 37 additions and 2 deletions
@@ -9480,9 +9480,11 @@ export function lowerForOf(lowerer: Lowerer, stmt: ts.ForOfStatement): IrStmt {
// A CHECKED-DYNAMIC iterable (`unknown[]`, the collapsed
// `(string | object)[]`, JSON-parsed values): a checked IteratorRecord
// binds each yielded dyn value.
// binds each yielded dyn value. Under `for await` (a JavaScript array
// literal mixing promises and plain values) the async-from-sync rules
// await each value.
if (iterable.type.kind === "dyn") {
return lowerForOfDyn(lowerer, stmt, iterable, labels);
return lowerForOfDyn(lowerer, stmt, iterable, labels, stmt.awaitModifier !== undefined);
}
if (tupleSource === null) {
lowerer.badType(stmt.expression, lowerer.typeOf(stmt.expression));
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/6600-npm-await-mixed-array/main.ts"
],
"diags": []
}
@@ -0,0 +1,6 @@
// for await over JavaScript array literals mixing promises and plain values
// (in a dependency): every value is awaited before the body runs, a break
// ends the loop, and a rejected element rejects the loop at that step.
import { run } from 'mixed-awaits';
for (const line of await run()) console.log(line);
@@ -0,0 +1 @@
export declare function run(): Promise<string[]>;
@@ -0,0 +1,19 @@
// for await over JavaScript array literals that mix promises and plain
// values: each value is awaited, as the async-from-sync iterator does.
'use strict';
async function run() {
var out = [];
for await (var p of [Promise.resolve('p1'), 'p2', Promise.resolve(3)]) out.push(typeof p + ':' + p);
for await (let q of [1, Promise.resolve({ n: 2 }), null]) out.push(JSON.stringify(q));
for await (const r of ['a', Promise.resolve('b'), 'c']) {
if (r === 'b') break;
out.push('r:' + r);
}
try {
for await (const s of ['ok', Promise.reject(new Error('rejected element')), 'never']) out.push('s:' + s);
} catch (error) {
out.push(error.message);
}
return out;
}
module.exports = { run };
@@ -0,0 +1 @@
{ "name": "mixed-awaits", "version": "1.0.0", "main": "index.js", "types": "index.d.ts" }