Files
scriptc/tests/corpus/8513-tuple-union-array.ts
T
Malte UblandClaude Opus 5.5 378901df95 Convert every arm of a mixed union into the array width
`["one"] | string[]` flowing into a `string[]` slot compiled to the
checked extraction against the array arm alone, so a tuple value threw at
runtime. Node accepts it: the value is an array, and nothing about a
`string[]` position distinguishes which arm produced it.

Let the union-width plan cover a mixed union that holds the destination
arm beside a convertible one, and place that plan before the
checked-extraction rule, so each arm converts on its tag (a tuple rebuilds,
an array copies). A union whose only non-unit arm is the destination
(`T[] | undefined`) still has nothing to convert and keeps its tested
extraction.

tests/corpus/8513-tuple-union-array.ts pins the binding, call-argument,
readonly, and single-tuple shapes against Node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:37:11 -07:00

26 lines
984 B
TypeScript

// A union that holds an array arm beside tuple arms, flowing into that array
// type: Node has one array either way, so both arms answer as arrays instead
// of the tuple arm trapping in the checked extraction.
type Mixed = ["one"] | string[];
function pick(flag: boolean): Mixed {
return flag ? ["one"] : ["two", "three"];
}
function take(value: string[]): string {
return `${value.length}:${value.join(",")}`;
}
for (const flag of [true, false]) {
const m = pick(flag);
const arr: string[] = m;
console.log(flag, arr.length, arr.join(","), take(m), arr[0]);
}
type Readonly = readonly ["a"] | readonly string[];
const r: Readonly = ["b", "c"];
const plain: readonly string[] = r;
console.log("readonly", plain.length, plain.join(","));
const narrow: ["one"] = ["one"];
const one: string[] = narrow;
console.log("single", one.length, one.join(","));
const optional: string[] | undefined = ["x"];
const lifted: string[] = optional!;
console.log("optional", lifted.length);