Difference between revisions of "Prevent child classes from redefining methods"
(Created page with "<syntaxhighlight lang="javascript"> class MyParentTokenClass extends Token { init(amount, owner) { const extended = this.constructor != MyParentTokenClass...") |
|||
| Line 1: | Line 1: | ||
| + | Here is a solution that we found to prevent child classes (using extends) to override some specific methods from your parent class. | ||
| + | |||
| + | Note that this has limitations though: it does NOT prevent someone to create a new function and implement any behavior they want. So while they may not be able to change how send() works, they can still create send2() and do anything they want in it. | ||
| + | |||
| + | We might be able to use this to prevent also adding any other function, essentially only allowing | ||
| + | |||
| + | |||
| + | <syntaxhighlight lang="javascript"> | ||
| + | class MyToken extends MyParentTokenClass {} | ||
| + | |||
| + | </syntaxhighlight> | ||
| + | |||
| + | |||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
Revision as of 06:41, 30 January 2026
Here is a solution that we found to prevent child classes (using extends) to override some specific methods from your parent class.
Note that this has limitations though: it does NOT prevent someone to create a new function and implement any behavior they want. So while they may not be able to change how send() works, they can still create send2() and do anything they want in it.
We might be able to use this to prevent also adding any other function, essentially only allowing
class MyToken extends MyParentTokenClass {}
class MyParentTokenClass extends Token {
init(amount, owner) {
const extended = this.constructor != MyParentTokenClass
if (!extended) throw new Error('This contract must be extended');
super.init(amount, owner);
// --- prevent redefining send() in child classes
// basic version (prevent overriding only one method like "send")
// const proto = Object.getPrototypeOf(this);
// if (proto.send !== MyParentTokenClass.prototype.send) {
// throw new Error(`Method 'send' cannot be overridden by class ${this.constructor.name}`);
// }
// advanced version, for ex to prevent overriding all methods
function getDeepPropertyNames(obj) {
const props = new Set(); // set to keep unique names
let current = obj;
while (current && current !== Object.prototype) {
// Get all string properties (including non-enumerable ones)
const ownProps = Object.getOwnPropertyNames(current);
ownProps.forEach(prop => props.add(prop));
// Move one level up the chain
current = Object.getPrototypeOf(current);
}
return Array.from(props);
}
const childProto = Object.getPrototypeOf(this);
const parentProto = Object.getPrototypeOf(childProto);
const childMethods = Object.getOwnPropertyNames(parentProto);
// console.log("childMethods:", childMethods);
const parentMethods = getDeepPropertyNames(MyParentTokenClass.prototype);
// console.log("parentMethods:", parentMethods)
let overriddenMethods = childMethods.filter(method =>
parentMethods.includes(method) && method !== 'constructor'
// && method !== 'init' // unsure if we want to allow overriding init() or not, might depend on case
);
console.log("overriddenMethods:", overriddenMethods);
if (overriddenMethods.includes('send')) {
throw new Error(
`Error: Subclass "${this.constructor.name}" is not allowed to override the critical methods: [${overriddenMethods}]. ` +
`These methods must remain as implemented in the base class.`
);
}
// console.log(`-- No forbidden overrides detected in "${this.constructor.name}". --\n`);
}
}