Accept optional checked-dynamic parameters in generic method instances (#1421)

This commit is contained in:
Chris Tate
2026-10-19 18:05:41 -05:00
committed by GitHub
3 changed files with 57 additions and 1 deletions
@@ -408,7 +408,15 @@ export function paramShapes(
return { type: mapped, mode: "rest" };
}
if (declParam.initializer) return defaultParameterShape(lowerer, declParam, mapped);
if (declParam.questionToken && !lowerer.bareUndefinedArmedUnion(mapped)) {
// A checked-value slot holds undefined directly, so an omitted
// argument needs no union arm (paramShape's declared-signature rule;
// an erased type parameter and a class-implemented interface view
// both map here).
if (
declParam.questionToken &&
!lowerer.bareUndefinedArmedUnion(mapped) &&
mapped.kind !== "dyn"
) {
lowerer.unsupported(
"SC1090",
declParam,
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/6340-prototype-reflection-optional-view-param.ts"
],
"diags": []
}
@@ -0,0 +1,42 @@
// A generic method whose optional parameter is an interface view (or an
// erased type parameter) reflects through the prototype of an instance
// that crossed into checked-dynamic code, and calls directly with the
// argument omitted.
interface Handle {
close(): string;
}
class Plain implements Handle {
close(): string {
return "closed";
}
}
class Registry {
private current: Handle | null = null;
use(handle: Handle | null): void {
this.current = handle;
}
wrap<F>(fn: F, handle?: Handle | null): F {
if (handle) this.current = handle;
return fn;
}
tag<T>(value: T, label?: T): string {
return `${String(value)}:${label === undefined ? "none" : String(label)}`;
}
size(): number {
return this.current === null ? 0 : 1;
}
}
const registry = new Registry();
const inc = registry.wrap((x: number) => x + 1);
console.log(inc(1), registry.size());
const dec = registry.wrap((x: number) => x - 1, new Plain());
console.log(dec(1), registry.size());
console.log(registry.tag(1), registry.tag("a", "b"));
const boxed: unknown = registry;
const proto = Object.getPrototypeOf(boxed);
console.log(Object.getOwnPropertyNames(proto).join(","));
console.log(typeof proto.wrap, typeof proto.tag, proto.size.call(registry));
const loose: any = registry;
console.log(loose.size(), proto.tag.call(registry, 2), proto.tag.call(registry, 2, 3));