|
| 1 | +// #7541 — `[...MyArr.from([1,2,3])]` threw `TypeError: value is not iterable`. |
| 2 | +// |
| 3 | +// The spread was never the problem: `Array.from` / `Array.of` / `Array.isArray` |
| 4 | +// are folded in the HIR on the LITERAL identifier `Array`, so a SUBCLASS |
| 5 | +// receiver matched nothing, and `js_class_static_method_call`'s miss-fallback |
| 6 | +// returns the RECEIVER — making `MyArr.from([1,2,3])` evaluate to the class ref, |
| 7 | +// which is genuinely not iterable. Directly-constructed instances always |
| 8 | +// spread fine; only the inherited-static-produced ones failed. |
| 9 | +// |
| 10 | +// KNOWN GAP, deliberately not asserted here: the property-GET form |
| 11 | +// (`typeof MyArr.from`) still reports `undefined` — only the CALL form is |
| 12 | +// dispatched. Same for `sub instanceof MyArr`, a pre-existing class-registry |
| 13 | +// parent-edge gap (#7575's Map/Set sibling). |
| 14 | + |
| 15 | +class MyArr extends Array {} |
| 16 | +class Indirect extends MyArr {} |
| 17 | + |
| 18 | +// The issue's exact repro. |
| 19 | +const sub = MyArr.from([1, 2, 3]); |
| 20 | +console.log([...sub]); |
| 21 | +console.log(Array.isArray([...sub])); |
| 22 | + |
| 23 | +// The statics themselves. |
| 24 | +console.log("from ", Array.isArray(sub), sub.length, sub.join(",")); |
| 25 | +const mapped = MyArr.from([1, 2, 3], (v: number) => v * 10); |
| 26 | +console.log("from+map ", mapped.length, mapped.join(",")); |
| 27 | +const fromSet = MyArr.from(new Set([4, 5, 6])); |
| 28 | +console.log("from set ", fromSet.length, fromSet.join(",")); |
| 29 | +const fromLike = MyArr.from({ length: 2, 0: "a", 1: "b" } as ArrayLike<string>); |
| 30 | +console.log("from like", fromLike.length, fromLike.join(",")); |
| 31 | +const ofd = MyArr.of(7, 8, 9); |
| 32 | +console.log("of ", ofd.length, ofd.join(",")); |
| 33 | +console.log("isArray ", MyArr.isArray([]), MyArr.isArray(1)); |
| 34 | + |
| 35 | +// An INDIRECT subclass resolves through the same chain walk. |
| 36 | +const ind = Indirect.from([1, 2]); |
| 37 | +console.log("indirect ", Array.isArray(ind), ind.length, ind.join(",")); |
| 38 | + |
| 39 | +// Every iteration surface on a static-produced instance. |
| 40 | +const it = MyArr.from([10, 20, 30]); |
| 41 | +const acc: number[] = []; |
| 42 | +for (const v of it) { |
| 43 | + acc.push(v); |
| 44 | +} |
| 45 | +console.log("for-of ", acc.join(",")); |
| 46 | +console.log("spread ", [...it].join(",")); |
| 47 | +console.log("Array.from", Array.from(it).join(",")); |
| 48 | +const [a0, a1] = it; |
| 49 | +console.log("destr ", a0, a1); |
| 50 | +console.log("map ", it.map((v) => v + 1).join(",")); |
| 51 | +console.log("index ", it[0], it[2], it.length); |
| 52 | + |
| 53 | +// Controls: the base intrinsic and a non-Array class are untouched. |
| 54 | +console.log("base from", Array.from([1, 2]).join(",")); |
| 55 | +console.log("base of ", Array.of(3, 4).join(",")); |
| 56 | +class Other { |
| 57 | + static make(): string { |
| 58 | + return "other"; |
| 59 | + } |
| 60 | +} |
| 61 | +class OtherSub extends Other {} |
| 62 | +console.log("user stat", OtherSub.make()); |
| 63 | +console.log("done"); |
0 commit comments