mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-02 07:43:42 +08:00
The keyed and tuple-index dispatches read the receiver and key inside a discard statement, which re-evaluated an effectful receiver expression (an `in` test over a call result) and ignored a key call that rebinds the receiver binding. Both now settle the receiver into a hidden local before the key evaluates, and the inherited-key probe returns its answer directly instead of wrapping it in a discard statement. Narrow the union field write's no-static-supertype path to unions that have a dynamic arm, so a union of unrelated static storage types keeps its refusal, matching the read path; a runtime-keyed spread into a record is covered by the checked extraction test instead of the refusal table. Review threads: - 4403396631 inherited-key probe evaluated an effectful receiver twice - 4403396650 key/index calls that reassign the receiver binding Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
98 lines
2.7 KiB
TypeScript
98 lines
2.7 KiB
TypeScript
// A RUNTIME index into a tuple: the element at an integer index in
|
|
// [0, arity), undefined for everything else — exactly Node's read of the
|
|
// underlying array (a fraction, a negative, NaN, Infinity, or one past the
|
|
// end all read undefined). The result joins the element types, so a
|
|
// heterogeneous tuple answers a union and a numeric consumer treats a miss
|
|
// like a missing array element.
|
|
|
|
type Vec3 = readonly [number, number, number];
|
|
|
|
const vec: Vec3 = [10, 20, 30];
|
|
|
|
function at(i: number): number | undefined {
|
|
return vec[i];
|
|
}
|
|
|
|
for (const i of [0, 1, 2, 3, -1, 1.5, NaN, Infinity]) {
|
|
console.log(i, at(i));
|
|
}
|
|
|
|
const mixed: [string, number, boolean] = ["s", 7, true];
|
|
for (const i of [0, 1, 2, 3]) {
|
|
console.log(i, mixed[i]);
|
|
}
|
|
|
|
// A literal index stays the plain positional read.
|
|
console.log(vec[0], mixed[1]);
|
|
|
|
// The asserted form of an index the program guarantees in range.
|
|
function sumTriple(i: number, j: number, k: number): number {
|
|
return vec[i]! + vec[j]! + vec[k]!;
|
|
}
|
|
console.log(sumTriple(0, 1, 2));
|
|
|
|
// A runtime index into a nested tuple read.
|
|
const rows: readonly (readonly [number, string])[] = [
|
|
[1, "a"],
|
|
[2, "b"],
|
|
];
|
|
function cell(r: number, c: number): number | string | undefined {
|
|
return rows[r]![c];
|
|
}
|
|
console.log(cell(0, 0), cell(1, 1), cell(0, 2));
|
|
|
|
// A tuple-typed call receiver evaluates once per read.
|
|
let calls = 0;
|
|
function make(): Vec3 {
|
|
calls++;
|
|
return [1, 2, 3];
|
|
}
|
|
let beyond: number = 3;
|
|
console.log(make()[1], make()[beyond], calls);
|
|
|
|
// The read still reaches a destination typed as the element type.
|
|
function double(x: number): number {
|
|
return x * 2;
|
|
}
|
|
console.log(double(vec[1]));
|
|
|
|
// Arithmetic on a miss is ToNumber(undefined) = NaN, as for a missing array
|
|
// element.
|
|
function plusOne(i: number): number {
|
|
return vec[i] + 1;
|
|
}
|
|
console.log(plusOne(1), plusOne(3));
|
|
|
|
// Records as elements join into a union result.
|
|
interface Wide {
|
|
w: number;
|
|
}
|
|
interface Tall {
|
|
t: string;
|
|
}
|
|
const recs: [Wide, Tall] = [{ w: 1 }, { t: "t" }];
|
|
function pick(i: number): Wide | Tall | undefined {
|
|
return recs[i];
|
|
}
|
|
for (const i of [0, 1, 2]) {
|
|
const r = pick(i);
|
|
console.log(i, r === undefined ? "missing" : "w" in r ? r.w : r.t);
|
|
}
|
|
|
|
// An optional element type inside the tuple keeps its own arm.
|
|
const opts: [number | undefined, number] = [undefined, 4];
|
|
function optAt(i: number): number | undefined {
|
|
return opts[i];
|
|
}
|
|
console.log(optAt(0), optAt(1), optAt(2));
|
|
|
|
// An index call that rebinds the receiver: JavaScript evaluates the receiver
|
|
// before the index, so the read observes the tuple the binding held then.
|
|
let active: Vec3 = [1, 2, 3];
|
|
const replacement: Vec3 = [4, 5, 6];
|
|
function swapKey(): number {
|
|
active = replacement;
|
|
return 1;
|
|
}
|
|
console.log(active[swapKey()], active[1], active[0]);
|