mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-02 07:43:42 +08:00
A function value's declared type may name fewer parameters than the function it holds, and a later widening call then passes arguments the declared type never spelled. JavaScript's function object carries all of its parameters, so nothing may be dropped on that path — that would be a silent divergence from Node. The narrowed store is already a named refusal (a compiled function value is one concrete native ABI: "'(string, number) => number' values where '(string) => number' is expected"), reported at the narrowing site for a binding and for an object-literal member slot. Record both in the diagnostics snapshot so the refusal cannot regress into a silent drop. Pin the shapes that DO keep every argument: a class member whose overloads spell fewer parameters than its implementation, the member read as a value (the read keeps the implementation's JS arity), and a value that entered through an untyped slot and is widened back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 94d44e947c48dc0184c941d526380ebd1b1b443d)
32 lines
1.3 KiB
TypeScript
32 lines
1.3 KiB
TypeScript
// A function holds ONE implementation, and a declared type only constrains
|
|
// call sites. An overloaded member's implementation may declare more
|
|
// parameters than any single overload spells; every overload's arguments
|
|
// still reach it — and so do the arguments of a later WIDENED call, because
|
|
// the value read out of the member keeps the implementation's full arity
|
|
// (JavaScript has no arity check; a missing argument is just undefined).
|
|
|
|
class Writer {
|
|
write(text: string): number;
|
|
write(text: string, times: number): number;
|
|
write(text: string, times?: number): number {
|
|
return text.length * (times === undefined ? 1 : times);
|
|
}
|
|
}
|
|
|
|
const writer = new Writer();
|
|
console.log(writer.write("abc"));
|
|
console.log(writer.write("abcd", 3));
|
|
|
|
// The member read as a value carries the same one implementation, so the
|
|
// two-argument call reaches its second parameter.
|
|
const writeValue = writer.write;
|
|
console.log(writeValue("abc"));
|
|
console.log(writeValue("abcd", 3));
|
|
|
|
// A value that entered through an untyped slot keeps the arity it was
|
|
// created with; widening it back delivers every argument.
|
|
const loose: any = (text: string, times: number) => text.length * times;
|
|
const widened = loose as (text: string, times: number) => number;
|
|
console.log(widened("abcd", 3));
|
|
console.log(widened("xy", 10));
|