Search code examples
javascriptfactory-pattern

Using spread in factory function instead of class `extends`


I have a replacement for class User ... as a factory function below:

const user = (name, age) => {
    return {
        name: name,
        age: age,
    }
}

I have an admin, too, that I would normally extend using ES6 classes. How would I do this using spread to give admin the properties of user as well?

const admin = (level) => {
    return {
       level: level,
    }
}

Solution

  • The general approach when using factory functions is one of composition. This is what takes the place of inheritance. To compose two different objects together, you could easily just spread the object props, like Eric Elliott's functional mixin approach:

    const user = (name, age) => ({ name, age })
    
    // The mixin approach
    const withAdminLevel = (level, user) => ({ ...user, level })
    
    console.log(withAdminLevel(1, user('John Doe', 30)))

    From that idea, though, we could also just use the user() factory function from inside admin(), instead of passing in a user. This could simplify your calls, but may not be as desirable in some cases (Ex. when you need to "upgrade" a user):

    const user = (name, age) => ({ name, age })
    
    // Use "user" factory function inside to simplify calls
    const admin = (level, ...args) => ({ ...user(...args), level })
    
    console.log(admin(1, 'John Doe', 30))

    Finally, we can combine these two:

    const user = (name, age) => ({ name, age })
    
    // The mixin approach
    const withAdminLevel = (level, user) => ({ ...user, level })
    
    // Use "user" factory function inside to simplify calls
    const admin = (level, ...args) => withAdminLevel(level, user(...args))
    
    const existingUser = user('John Doe', 30)
    
    console.log(withAdminLevel(1, existingUser)) // upgrade
    console.log(admin(2, 'Jane Doe', 28)) // still simple to do