immutable.js
function extend(to_extend, new_defaults = {}) {

    let new_default_props = Object.getOwnPropertyNames(new_defaults);
    let old_props = to_extend.getImmutablePropertyNames();
    let new_props = new_default_props.filter(e => old_props.indexOf(e) < 0);
    //let overriden_props = new_default_props.filter(e => old_props.indexOf(e) >= 0);
    let props = old_props.concat(new_props);

    debug('merged props', props);
    
    /** Immutable class created from new_defaults and the extended class
     *
     */
    let immutableClass = class extends to_extend {

        /** Constructor.
         *
         * Can take a single object argument, in which case the properties of the object are copied over into
         * any matching default propertis that were defined in 'create'. Can also take normal arguments, in which
         * case the arguments are matched to the default properties in the base class, and then to additional 
         * properties defined in the new_defaults object .
         */
        constructor() {

            // Handle case where we have one argument that's an object: assume it's properties to pass in
            let values = processArguments(props, arguments, new_defaults);

            // Handle values for base class
            super(values);
            // assign values to properties in order
            for (let prop of new_props) {
                let value = values[prop];
                debug('value', value);
                Object.defineProperty(this, prop, { value, enumerable: true, writable: false });
            }           
        }

        /** Get the immutable property names
         *
         * An immutable object may legitimately have transient proprerties that are not part of the public
         * interface of the object. We therefore create a getImmutablePropertyNames static method that gets
         * the list of immutable properties names that is defined for this class.
         */
        static getImmutablePropertyNames() { return props; }


        /** Extends an immutable object, making a new immutable object.
         *
         * @param new_defaults additional properties for the extended object, plus default values for them.
         */
        static extend(new_defaults) { return extend(immutableClass, new_defaults)}
    };

    for (let prop of new_props) {
        let setterName = 'set' + prop.slice(0,1).toUpperCase() + prop.slice(1);
        immutableClass.prototype[setterName] = function(val) {
            debug(setterName, prop, val);
            return this.merge({ [prop] : val });
        };
    }

    return immutableClass;  
}
function extend(to_extend, new_defaults = {}) {

    let new_default_props = Object.getOwnPropertyNames(new_defaults);
    let old_props = to_extend.getImmutablePropertyNames();
    let new_props = new_default_props.filter(e => old_props.indexOf(e) < 0);
    //let overriden_props = new_default_props.filter(e => old_props.indexOf(e) >= 0);
    let props = old_props.concat(new_props);

    debug('merged props', props);
    
    /** Immutable class created from new_defaults and the extended class
     *
     */
    let immutableClass = class extends to_extend {

        /** Constructor.
         *
         * Can take a single object argument, in which case the properties of the object are copied over into
         * any matching default propertis that were defined in 'create'. Can also take normal arguments, in which
         * case the arguments are matched to the default properties in the base class, and then to additional 
         * properties defined in the new_defaults object .
         */
        constructor() {

            // Handle case where we have one argument that's an object: assume it's properties to pass in
            let values = processArguments(props, arguments, new_defaults);

            // Handle values for base class
            super(values);
            // assign values to properties in order
            for (let prop of new_props) {
                let value = values[prop];
                debug('value', value);
                Object.defineProperty(this, prop, { value, enumerable: true, writable: false });
            }           
        }

        /** Get the immutable property names
         *
         * An immutable object may legitimately have transient proprerties that are not part of the public
         * interface of the object. We therefore create a getImmutablePropertyNames static method that gets
         * the list of immutable properties names that is defined for this class.
         */
        static getImmutablePropertyNames() { return props; }


        /** Extends an immutable object, making a new immutable object.
         *
         * @param new_defaults additional properties for the extended object, plus default values for them.
         */
        static extend(new_defaults) { return extend(immutableClass, new_defaults)}
    };

    for (let prop of new_props) {
        let setterName = 'set' + prop.slice(0,1).toUpperCase() + prop.slice(1);
        immutableClass.prototype[setterName] = function(val) {
            debug(setterName, prop, val);
            return this.merge({ [prop] : val });
        };
    }

    return immutableClass;  
}
function extend ( to_extend , new_defaults = { } ) { let new_default_props = Object . getOwnPropertyNames ( new_defaults ) ; let old_props = to_extend . getImmutablePropertyNames ( ) ; let new_props = new_default_props . filter ( e => old_props . indexOf ( e ) < 0 ) ; //let overriden_props = new_default_props.filter(e => old_props.indexOf(e) >= 0); let props = old_props . concat ( new_props ) ; debug ( 'merged props' , props ) ; /** Immutable class created from new_defaults and the extended class
     *
     */ let immutableClass = class extends to_extend { /** Constructor.
         *
         * Can take a single object argument, in which case the properties of the object are copied over into
         * any matching default propertis that were defined in 'create'. Can also take normal arguments, in which
         * case the arguments are matched to the default properties in the base class, and then to additional 
         * properties defined in the new_defaults object .
         */ constructor ( ) { // Handle case where we have one argument that's an object: assume it's properties to pass in let values = processArguments ( props , arguments , new_defaults ) ; // Handle values for base class super ( values ) ; // assign values to properties in order for ( let prop of new_props ) { let value = values [ prop ] ; debug ( 'value' , value ) ; Object . defineProperty ( this , prop , { value , enumerable : true , writable : false } ) ; } } /** Get the immutable property names
         *
         * An immutable object may legitimately have transient proprerties that are not part of the public
         * interface of the object. We therefore create a getImmutablePropertyNames static method that gets
         * the list of immutable properties names that is defined for this class.
         */ static getImmutablePropertyNames ( ) { return props ; } /** Extends an immutable object, making a new immutable object.
         *
         * @param new_defaults additional properties for the extended object, plus default values for them.
         */ static extend ( new_defaults ) { return extend ( immutableClass , new_defaults ) } } ; for ( let prop of new_props ) { let setterName = 'set' + prop . slice ( 0 , 1 ) . toUpperCase ( ) + prop . slice ( 1 ) ; immutableClass . prototype [ setterName ] = function ( val ) { debug ( setterName , prop , val ) ; return this . merge ( { [ prop ] : val } ) ; } ; } return immutableClass ; }
Extend an immutable class

The resulting class will have the supplied properties and defaults, a constructor, an additional setters
which return a copy of the instance they are applied to with the implied update in place.

Passing in { property: undefined } will ensure a setter is created for 'property' without creating a default.

@param to_extend a class object (which must inherit from an immutable object as created above)
@param defaults {Object} A set of key/value pairs used to create additional default properties for class instances.
@returns A class object with appropriate properties and setter methods.
Extend an immutable class