-
Notifications
You must be signed in to change notification settings - Fork 12.8k
readonly
fields should be writable: false
#44357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
readonly
fields should readonly
fields should be writable: false
You checked “This could be implemented without emitting different JS based on the types of the expressions” but isn’t this specifically asking for different JS based on information from the static type system? |
Maybe I misunderstood what that meant, but to me a field declaration is not an expression. It is a different kind of parser entry. Namely, a declaration. |
Well, I expect this will be declined as it does not allow type erasure. |
How is it not allowing type erasure? There is already a well defined JS way of doing just this. I was slightly surprised that turning on |
Also be aware that readonly properties are implicitly compatible with the mutable counterpart. Your class can implement interfaces where the property is not marked |
Hopefully this is possible at runtime with the new TC39 decorators proposal, and there would be a way for TypeScript to recognize the property descriptor modifications made by the decorator so the readonly keyword isn't necessary. |
It is similar to how |
I don't quite understand this, yes the configuration is done at run time but it really should be done in a constructor. The annoying thing is is that without this proposal, the equivalent code is rather obtuse: With proposal: class Foo {
readonly foo = "1";
}
const f = new Foo();
(f as any).foo = "2"; // error Current situation: interface Foo {
readonly foo: string;
}
class Foo {
constructor() {
Object.defineProperty(this, "foo", {
enumerable: true,
configurable: true,
writable: false,
value: "1"
});
}
}
const f = new Foo();
(f as any).foo = "2"; // error The current situation is necessary so that a second |
But why should that be an error? class Foo { readonly foo = "1"; }
class MFoo { foo = "1"; }
function changeFoo(x: MFoo) { x.foo = "abc"; }
const foo = new Foo();
changeFoo(foo); |
Well that is what I am proposing. Namely a change. And furthermore, I know that you can change the |
Because currently the
If you're suggesting that the Or am I missing what we are trying to achieve here? |
The only non-type-directed-emit case I can see here would be class fields, and |
I'm guessing it we asked for a "nonwritable" tag that would emit code with { writable: false } this wouldn't be accepted due to being out-of-scope (as not a typings issue)? Just checking. |
The only way we'd do that is if |
Sure, thanks. |
It would be possible to emit Example with useDefineForClassFields: trueclass Super {
readonly field: number = 1;
readonly otherField: number = 0;
constructor() {
this.field = 2;
}
}
class Sub extends Super {
readonly field: number = 3;
constructor() {
const value = 4;
super();
this.field = value;
}
} "use strict";
class Super {
constructor() {
// field init
Object.defineProperty(this, "field", {
enumerable: true,
configurable: true,
writable: true,
value: 1
});
Object.defineProperty(this, "otherField", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
// constructor
this.field = 2;
// after constructor: make all readonly properties non-writable
Object.defineProperties(this, {
field: { writable: false },
otherField: { writable: false },
});
}
}
class Sub extends Super {
constructor() {
// part of constructor up to super call
const value = 4;
super();
// field init
Object.defineProperty(this, "field", {
enumerable: true,
configurable: true,
writable: true,
value: 3
});
// part of constructor after super call
this.field = value;
// after constructor: make all readonly properties non-writable
Object.defineProperties(this, { field: { writable: false } });
}
} When Example with useDefineForClassFields: falseclass Super {
readonly field: number = 1;
readonly otherField: number = 0;
constructor() {
this.field = 2;
}
}
class Sub extends Super {
readonly field: number = 3;
constructor() {
const value = 4;
super();
this.field = value;
}
} "use strict";
class Super {
constructor() {
// field init
this.field = 1;
this.otherField = 0;
// constructor
this.field = 2;
// after constructor: make all readonly properties non-writable
Object.defineProperties(this, {
field: { writable: false },
otherField: { writable: false },
});
}
}
class Sub extends Super {
constructor() {
// part of constructor up to super call
const value = 4;
super();
// after super call: make all properties that override readonly properties writable
Object.defineProperty(this, { field: { writable: true } });
// field init
this.field = 3;
// part of constructor after super call
this.field = value;
// after constructor: make all readonly properties non-writable
Object.defineProperties(this, { field: { writable: false } });
}
} It would be great if this could be a compiler option @RyanCavanaugh |
@c-harding that would make this violate this guideline:
I think a decorator would be the TC39-approved way of accomplishing this. |
@justinfagnani How would such a decorator look, such that it is possible to set the property in the constructor but no later? |
@justinfagnani I had a go in this gist here, but it has slightly different semantics: I had to disallow overriding readonly properties, as it wasn’t possible to allow this using a decorator. Additionally, it seems like I would have to annotate both the field and the class (in addition to the |
Suggestion
When
useDefineForClassFields
is enabledreadonly
andstatic readonly
fields should be emitted withwritable: false
.🔍 Search Terms
✅ Viability Checklist
My suggestion meets these guidelines:
⭐ Suggestion
Emit
writable: false
when a field isreadonly
anduseDefineForClassFields
is enabled📃 Motivating Example
readonly
fields now are runtime readonly too.The text was updated successfully, but these errors were encountered: