10

How can I modify a class instance using the spread operator, while keeping its constructor the same?

The question may be clearer with an example.

class User {
    constructor({ name = "", age = 0 }) {
        this.name = name
        this.age = age
    }

    getName() {
        return this.name
    }

    getAge() {
        return this.age
    }
}

Creating a regular instance works fine:

const u1 = new User({ name: 'John Doe', age: 33 })
console.log(u1.getAge()) // 33

What does not work is using the spread operator with the instance:

const u2 = { ...u1, age: 34 }
console.log(u2.getAge()) // 

I read that the spread operator only copies own enumerable properties, so I also tried this:

const u3 = { ...u1, ...User.prototype, age: 34 }
console.log(u3.getAge())

This works better (it allows me to call the methods in User), but still breaks when using instanceof or Object.isPrototypeOf():

console.log(u1 instanceof User) // true
console.log(u2 instanceof User) // false
console.log(u3 instanceof User) // false

console.log(User.isPrototypeOf(u1)) // true
console.log(User.isPrototypeOf(u2)) // false
console.log(User.isPrototypeOf(u3)) // false

I also tried these, but they didn't work either:

const u4 = Object.assign({ age: 34 }, u1)
const u5 = Object.assign({ age: 34 }, u1, User.prototype)

So, TL:DR, is it possible to create a copy of a class instance which changes some properties of the parent, but keeps a reference to its constructor?

Hope I made myself clear. Thanks in advance for the help!

7
  • 2
    Because you're creating a new plain old javascript object. Aren't the braces { and } a clue? Commented Jun 20, 2020 at 16:00
  • 1
    Hi, why don't you just create setter function for to change values? As far as i understand cause you are creating a new object it's not gonna be an instance of your User class. Commented Jun 20, 2020 at 16:01
  • 2
    The closest to what you want using the spread syntax is: const u6 = new User({ ...u1, age: 34 }); Commented Jun 20, 2020 at 16:03
  • In general, I wouldn’t recommend doing this as a caller. The type should know how to copy itself, i.e. implement either .clone() or .withAge() methods, implemented like withAge(newAge) { return new User({name: this.name, age: newAge}); }, for const u2 = u1.withAge(34). Commented Jun 20, 2020 at 16:04
  • thanks, that's helpful. Any better way to do this, though? Commented Jun 20, 2020 at 16:04

1 Answer 1

12

With the { } object literal notation you always create a plain object, never an instance of your custom class. You should use the new operator to get a new instance (or Object.create):

class User {
    constructor({ name = "", age = 0 }) {
        this.name = name
        this.age = age
    }

    getName() {
        return this.name
    }

    getAge() {
        return this.age
    }
}

const u1 = new User({ name: 'John Doe', age: 33 })
console.log(u1.getName(), u1.getAge()) // John Doe, 33

const u2 = new User({ ...u1, age: 34 });
console.log(u2.getName(), u2.getAge()) // John Doe, 34

Sign up to request clarification or add additional context in comments.

1 Comment

Good if constructor accepts enumerable properties of its own instance, but if it does not, you'd need to construct the new instance with valid options, rather than a spread version of its own instance.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.