Files
scriptc/tests/corpus/6246-map-set-function-values.ts
T
Malte UblandClaude Opus 5.5 f4a8511dca Compile maybe-promise unions, unknown[] rest parameters, and function-valued Maps and Sets
A union may now hold one promise beside data members (`T | Promise<T>`,
`Promise<R | void> | R | void`). The union tag is the `instanceof Promise`
test, and every operation keeps Node's microtask timing: `await` awaits the
promise arm or takes the one non-thenable turn, an async `return` (block
bodies, concise arrows, then handlers) adopts the promise arm after the
function's own finally blocks, `Promise.resolve` returns the promise arm as
is, and `typeof (u as any).then` reads the tag. Data arms that declare
`then`, two promise arms, and function arms beside a promise stay refused
with diagnostics. Inferred JavaScript literals that mix promises and values
keep the checked-dynamic array.

Rest parameters typed `unknown[]` or `any[]` in TypeScript use the
checked-dynamic variadic ABI that JavaScript rest parameters already use,
including spread forwarding (`(...args) => handler(...args)`).

Functions are now Map values, Map keys and Set elements, compared by
closure identity.

Two pre-existing bugs in the same area are fixed: an async lambda in a
`Promise<void> | void` slot got a `Promise<union>` ABI and threw when called
through the slot, and `return cb()` of such a callback in an async function
dropped the promise instead of adopting it.

The retired-engine 2251 and 2790 diagnostics fixtures (tagged templates
with `any[]` rest tags) now compile and match Node, so they move to the
corpus as 6248 and 6249.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:58:57 -07:00

53 lines
1.8 KiB
TypeScript

// Functions as Map values, Map keys and Set elements: factory and listener
// registries keep each closure's identity, and a handler that captures its
// own registry is collected with it.
interface Extension {
name: string;
run(x: number): number;
}
type ExtensionFactory = (api: { log: (s: string) => void }) => Extension;
const factories = new Map<string, ExtensionFactory>();
factories.set("double", (api) => ({
name: "double",
run: (x) => {
api.log("double " + x);
return x * 2;
},
}));
factories.set("inc", () => ({ name: "inc", run: (x) => x + 1 }));
const lines: string[] = [];
const api = { log: (s: string) => lines.push(s) };
for (const [key, make] of factories) {
const ext = make(api);
console.log(key, ext.name, ext.run(10));
}
console.log(factories.get("missing") === undefined, typeof factories.get("inc"), factories.has("inc"));
const inc = factories.get("inc");
if (inc) console.log(inc(api).run(41));
factories.delete("inc");
console.log([...factories.keys()].join(","), factories.size, lines.join(";"));
type Listener = (v: string) => void;
const listeners = new Set<Listener>();
const a: Listener = (v) => console.log("a", v);
const b: Listener = (v) => console.log("b", v);
listeners.add(a);
listeners.add(b);
listeners.add(a);
console.log(listeners.size, listeners.has(a), listeners.has((v) => console.log(v)));
for (const l of listeners) l("x");
listeners.delete(a);
listeners.forEach((l) => l("y"));
const counts = new Map<Listener, number>();
counts.set(a, 1);
counts.set(b, 2);
counts.set(a, 3);
console.log(counts.size, counts.get(a), counts.get(b));
const registry = new Map<string, () => number>();
registry.set("self", () => registry.size);
registry.set("double", () => registry.size * 2);
console.log([...registry.values()].map((f) => f()).join(","));