To achieve this goal, one can utilize a "smart" setter approach. The concept involves defining a setter within the class, removing the setter from within the class itself, and creating a read-only property using the Object.defineProperty
method with a descriptor where the writable
property is set to false
.
The aspect of being "changed within the class itself" can be accomplished through redefining another property. It is important to ensure that the property is initially made configurable
.
Below is an example of how this process could look (there might not be a more elegant solution) - note that this runtime version of the answer is not specific to TypeScript:
class ReadonlyFields {
set fixed(value) {
Object.defineProperty(this, "fixed", {
value,
writable: false,
configurable: true,
});
}
doSomething() {
Object.defineProperty(this, "fixed", {
value: 24, //exact logic for changing the value is up to you
writable: false
});
}
}
const r = new ReadonlyFields();
r.fixed = 42;
console.log(r.fixed); //42;
r.fixed = 24;
console.log(r.fixed); //42;
r.doSomething();
console.log(r.fixed); //24;
If enforcing this behavior from a type system perspective is desired, simply make the property readonly
:
class ReadonlyFields {
readonly fixed!: number;
setFixed(value: number) {
Object.defineProperty(this, "fixed", {
value,
writable: false,
configurable: true,
});
}
doSomething() {
Object.defineProperty(this, "fixed", {
value: 24, //exact logic for changing the value is up to you
writable: false,
});
}
}
const r = new ReadonlyFields();
r.setFixed(42); //OK
//@ts-expect-error
r.fixed = 24;
r.doSomething(); //OK