Prevent child classes from redefining methods

From RunWiki
Revision as of 06:41, 30 January 2026 by Zhell (talk | contribs)
Jump to: navigation, search

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`);
    }
}