-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Description
Suggestion
Add ability to specify that some class property is read-only for users outside the class, but it should still be writable by the code within the class.
This behaviour can be achieved with getters and setter, but: (1) it is very verbose; (2) and there is performance penalty.
class A {
#__foo: string;
public get foo(): string {
return this.#__foo;
}
}Instead, would be nice if this behaviour could be achieved with a single line of code:
class A {
public readonly? foo: string;
}Note ? in readonly?. The ? could indicate a new flavor of readonly keyword, which makes the property read-only for external users, but still writable for code inside the class.
✅ Viability Checklist
My suggestion meets these guidelines:
- 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 feature would agree with the rest of TypeScript's Design Goals.
⭐ Suggestion
I see there could be two implementation approaches.
Option 1: No runtime changes, only type checking
Here the compiler would emit the same JavaScript code as if the readonly? keyword was not present. The only difference would be errors in TypeScript, if somebody tries to modify the property from outside the class.
Option 2: Enforcing it
TypeScript could rewrite the property to enforce it being read-only from outside the class, but still writable from within the class.
This TypeScript code:
class A {
public readonly? foo: string = '';
}Would result in JavaScript like this:
class A {
constructor() {
this.#$$foo = '';
}
get foo() {
return this.#$$foo;
}
}💻 Use Cases
Consider this controllable Promise class—Defer, to avoid getter/setter performance penalty and still have TypeScript's readonly annotation, one needs to cast this to any.
export class Defer<T> {
public readonly resolve!: (data: T) => void;
public readonly reject!: (error: any) => void;
public readonly promise: Promise<T> = new Promise<T>((resolve, reject) => {
(this as any).resolve = resolve;
(this as any).reject = reject;
});
}With the proposed change, the code would change to
export class Defer<T> {
public readonly? resolve!: (data: T) => void;
public readonly? reject!: (error: any) => void;
public readonly promise: Promise<T> = new Promise<T>((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}