Prevent child classes from redefining methods

From RunWiki
Revision as of 06:38, 30 January 2026 by Zhell (talk | contribs) (Created page with "<syntaxhighlight lang="javascript"> class MyParentTokenClass extends Token { init(amount, owner) { const extended = this.constructor != MyParentTokenClass...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
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`);
    }
}