Files
scriptc/tests/corpus/6384-variadic-wrapper-nested.ts
T
Malte UblandClaude Opus 5.5 cfb50d16e2 Specialize variadic generic wrappers to the wrapped function's signature
Higher-order wrappers over `(...args: any) => any` that forward `arguments`
with `fn.apply(this, arguments)` and type their options with
`Parameters<F>` / `ReturnType<F>`, and their rest-tuple rewrite, did not
compile. Generic calls already monomorphize per call site; these pieces now
resolve to that site's concrete signature:

- Rest parameters typed as fixed tuples (`...args: [A, B]`, `...args: A`
  bound to a tuple, `...args: Parameters<F>`) flatten to positional
  parameters, so `(...args: [A, B]) => R` and `(a: A, b: B) => R` share one
  function type. Trailing optional positions become `T | undefined` slots.
- `Parameters<F>`, `ReturnType<F>`, `Awaited<ReturnType<F>>` and the
  `ReturnType<F> extends PromiseLike<infer U> ? U : …` spelling resolve
  through F's binding.
- A parameterless function expression reading `arguments` lowers against
  the fixed signature its value is asserted to (or contextually typed as);
  `arguments` is that signature's tuple.
- `f.apply(thisArg, args)` and `f(...args)` over a forwarded tuple call
  directly, passing `thisArg` as the receiver. A tuple that is only
  forwarded is never allocated.
- `fn.name` on a function parameter folds to the call site's static name;
  the instantiation key carries it.

Also: optional callback fields narrowed at the call (`o.cb ? o.cb(x) : …`)
compile, effect-free surplus arguments to fixed-arity function values are
dropped, and `arguments.length` no longer counts a `this` parameter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:03:44 -07:00

38 lines
1.1 KiB
TypeScript

// Nested variadic wrappers: wrapping an already wrapped function, mixing
// the `arguments` form and the rest-tuple form. The inner wrapper's
// function binding gives the outer wrapper its `fn.name`.
type AnyFn = (...args: any) => any;
function trace<F extends AnyFn>(fn: F, label?: string): F {
const wrapped = function (this: unknown) {
console.log("trace>", label ?? fn.name);
const out = fn.apply(this, arguments as any);
console.log("trace<", label ?? fn.name);
return out;
};
return wrapped as unknown as F;
}
function timed<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R {
return function (this: unknown, ...args: A): R {
console.log("timed>", fn.name, args.length);
return fn.apply(this, args);
};
}
function area(w: number, h: number): number {
return w * h;
}
const twice = trace(trace(area));
console.log(twice(2, 3));
const labelled = trace(trace(area, "inner"), "outer");
console.log(labelled(4, 5));
const mixed = timed(trace(area));
console.log(mixed(6, 7));
const mixed2 = trace(timed(area));
console.log(mixed2(1, 9));