Default parameter values are not applied when a method is called through dynamic dispatch. A call through an interface-typed or any-typed reference passes only the arguments written at the call site; the missing ones arrive as undefined and the parameter initializer never runs.
Repro: https://makecode.com/_EjWTcAMaqhyX
interface Shape {
scaled(x: number, factor?: number): number;
}
class Thing implements Shape {
scaled(x: number, factor = 5) {
return x * factor;
}
}
const t = new Thing();
const s: Shape = t;
console.log(t.scaled(2)); // 10 -- static call, default applied
console.log(s.scaled(2)); // NaN -- same object via interface: default not applied
Root cause is in the emitter, not a backend: defaults are filled in at the call site from the statically known signature (addDefaultParametersAndTypeCheck). A dynamically dispatched call site has no signature, so nothing fills the argument, and there is no callee-side default application to catch it -- the arity wrapper pads with undefined. A fix likely means emitting callee-side default application for methods reachable through dynamic dispatch.
It seems like a not-recent bug: reproduces on pxt v12.3.14 (the release prior to my recent optimizations), and the call-site filling mechanism dates to 2017.
A ready-made red test is checked in disabled: uncomment the REPRO block in tests/compile-test/lang-test0/57defaultparamdispatch.ts (added in #11561) and gulp testlang fails with qzdp:iface. The file's active assertions pin the behavior that must hold regardless of the fix (concrete-path defaults, explicit-argument calls, and agreement between the interface and any paths).
Workaround: pass every argument explicitly at dynamic call sites, or have the callee test for undefined itself.
Default parameter values are not applied when a method is called through dynamic dispatch. A call through an interface-typed or
any-typed reference passes only the arguments written at the call site; the missing ones arrive asundefinedand the parameter initializer never runs.Repro: https://makecode.com/_EjWTcAMaqhyX
Root cause is in the emitter, not a backend: defaults are filled in at the call site from the statically known signature (
addDefaultParametersAndTypeCheck). A dynamically dispatched call site has no signature, so nothing fills the argument, and there is no callee-side default application to catch it -- the arity wrapper pads withundefined. A fix likely means emitting callee-side default application for methods reachable through dynamic dispatch.It seems like a not-recent bug: reproduces on pxt v12.3.14 (the release prior to my recent optimizations), and the call-site filling mechanism dates to 2017.
A ready-made red test is checked in disabled: uncomment the REPRO block in
tests/compile-test/lang-test0/57defaultparamdispatch.ts(added in #11561) andgulp testlangfails withqzdp:iface. The file's active assertions pin the behavior that must hold regardless of the fix (concrete-path defaults, explicit-argument calls, and agreement between the interface andanypaths).Workaround: pass every argument explicitly at dynamic call sites, or have the callee test for
undefineditself.