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 usingnew 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.)