Home > other >  spread operator for object containing readonly
spread operator for object containing readonly

Time:02-14

My sample class:

class Foo {
  private static counter = 1;

  readonly id: number;

  constructor() {
    this.id = Foo.counter  ;
  }
}

I'm having 2 options:

  • Make the constructor accepts another Foo, so that I can clone using new Foo(foo)
  • Make id public, so that I can clone using { ...foo }

Is there anyway to use spread clone AND keep readonly (or anything that prevents id from being modified from outside)?

CodePudding user response:

Is there anyway to use spread clone AND keep readonly...

No. The result of { ...foo } is a plain object with read/write public data properties for the own, enumerable properties of foo. There's nothing you can add to your class to change that behavior.

Also note that because { ...foo } creates a plain object, it will no longer be an instance of Foo. If Foo defined any methods, for instance, the clone wouldn't have them. (In the code in the question, that kind of clone would be assignment-compatible with Foo because it doesn't define any methods. But if Foo had methods, it wouldn't be.)

If you want to use spread syntax (it's not an operator) to clone objects, don't use class, use interface since the clones won't inherit from the prototype the class will assign.

In this case, as you said, you can make Foo's constructor optionally a copy-constructor:

class Foo {
    private static counter = 1;
  
    readonly id: number;
  
    constructor(other?: Foo) {
        if (other) {
            // Copying
            this.id = other.id; // Just to make TypeScript happy that `id` is
                                // definitely assigned on this path (it would be
                                // anyway, but TypeScript doesn't know that)
            Object.assign(this, other);
        } else {
            // Creating a new one
            this.id = Foo.counter  ;
        }
    }
}

(Instead of the this.id = other.id; line, we could have marked id with the "definitely assigned" annotation [readonly id!: number;]. But since there are multiple paths through the constructor, I wouldn't do that, because it's too easy to forget about it when modifying the code. So I'd use the unnecessary assignment above.)

  • Related