A tricky thing I could see without ever explicitly defining "multiplier" (e.g. on the object prototype) is passing a Proxy that e.g. has a fallback for all missing properties. Detecting a proxy is only kind of possible (?) but we can copy all the original target properties from it, which should make it safe.
So here goes, my safe solution for modifying function signature in a non breaking way:
function add({ ...args }) {
const { a, b, ...rest } = args;
if (typeof a !== "number" || typeof b !== "number") {
throw "all arguments must be numbers";
}
if (Object.keys(rest).length > 0) {
throw "You may only pass arguments a and b";
}
return a + b;
}
function _add({ ...args }) {
const { a, b, multiplier = 1, ...rest } = args;
if (
typeof a !== "number" ||
typeof b !== "number" ||
typeof multiplier !== "number"
) {
throw "all arguments must be numbers";
}
if (Object.keys(rest).length > 0) {
throw "You may only pass arguments a, b and multiplier";
}
return a + b * multiplier;
}
Most of these issues (not the proxy one) should be solved by typescript.