Description
π Search Terms
readonly methods
β Viability Checklist
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
β Suggestion
I suggest using the readonly keyword for methods, indicating that they don't change the contents of the "this" object, but only receive data. Similar to const methods in C++. This will provide good security from possible errors, as well as better understanding of the code:
class A {
private value = 10;
getValue() readonly { // mark the method as readonly, prohibiting changing the contents of the object
this.value = 0; // Error: can't modify readonly object
this.setValue(0); // Error: can't call a mutating method on a readonly object
return this.value;
}
setValue(val : number) {
this.value = val; // OK
}
}
And the types themselves can also be marked as readonly, meaning that all their contents become readonly, and only readonly methods are available:
function foo(a : readonly A) {
a.setValue(0); // Error: can't call a mutating method on a readonly object
a.getValue(); // OK
}
We already have such syntax for declaring readonly arrays. And it will become a universal declaration for any readonly objects.
And if the method does not change the contents of the class, but returns a reference to some internal object of the class, then we can overload the signature of this method in two versions (with and without readonly):
getData() readonly : readonly number[];
getData() : number[];
It is also necessary to clarify that the readonly type means deep readonly of the entire nested structure of the object, not just the first-level properties. And if we need a different behavior for individual properties, we can overload the getters for them, just like in the example above.
As an additional option, we can use readonly for the declared interface or class as a whole, eliminating the need to write it for each method:
interface ReadonlySome readonly { // mark the interface as readonly
getValue() : number; // becomes a readonly method
calculate(data: readonly number[]) : number; // becomes a readonly method
}
π Motivating Example
π» Use Cases
As I mentioned above, this concept is implemented in C++, but using the const keyword:
class A {
int _value = 10;
int getValue() const {
_value = 0; // Error
setValue(0); // Error
return _value;
}
void setValue(int val) {
_value = val; // OK
}
foo(const A& a) {
a.setValue(0); // Error
}
};
It is also partially implemented in C#, but in a very limited version. I don't see any reason why it couldn't be fully implemented.
In TypeScript as an alternative we can try to wrap "this" in some kind of typing:
class A {
readonlyMethod(this : SomeReadonlyWrap<A>) { }
}
but I haven't found the right implementation of this wrapper yet. And anyway, it's very cumbersome to write it like that for all methods, especially in template classes.