-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Different access modifier for getter and setter #2845
Comments
I believe I should also move my example here from related issue: declare abstract class Emitter {
new (): Emitter;
on: (name:string, handler: (arg:any) => void) => void;
off: (name:string, handler: (arg:any) => void) => void;
protected emit: (name:string, arg:any) => void;
}
class Person extends Emitter {
constructor(name:string) {
super();
this.name = name;
}
private _name:string;
get name() {
return this._name;
}
set name(value) {
if (this._name !== value) {
this._name = value;
this.emit('change:name', value);
//this way is better
this.updatedAt = new Date();
//than this way
this.setUpdatedAt(new Date());
}
}
////
private _updatedAt:Date;
get updatedAt() {
return this._updatedAt;
}
private set updatedAt(value) { //Getter and setter do not agree in visibility
//some logic and a simple readonly (our absence of a setter) is not enough
if (this._updatedAt !== value) {
this._updatedAt = value;
this.emit('change:updatedAt', value);
}
}
//// method implementation - but what's the point in it?
private setUpdatedAt(value) {
if (this._updatedAt !== value) {
this._updatedAt = value;
this.emit('change:updatedAt', value);
}
}
}
const entity = new Person('Mike');
entity.on('change:updatedAt', console.log.bind(console));
entity.name = 'Thomas';
//but manually setting updatedAt should be forbidden
entity.updatedAt = new Date(); //restricted Here, property I agree that private setter is just a syntax sugar for private methods with additional logic (like emitting) but I think it is inconsistent when part of logic related to a field is in a property (getter) and another in a method (private setter). This way getters/setters are implemented in |
The recommendation here is to use a public readonly getter and a private/protected backing field. Accessors are symetrical with properties in the type system. anything we do will need to be manifested in the type and expressible on properties. Adding new access modifiers to enable private_set/public_get would increase the complexity of the language and the learning curve, and the value gained from this would not match the added complexity. |
Anyway adding my +1 for this, in case it is considered again in future... |
I'd still like to see this. C# has it, and it's incredibly useful in many circumstances, especially when you have flags that want to be read externally to the class, but only set internally with private/protected. I think the suggestion that it isn't used all that often is a farce in that the pattern isn't used because it's not available to use. |
This is a good addition to the language: The amount of complexity that this would add (from the perspective of a typescript programmer) is small. The concept is easy to understand and the compiler should be able to provide good error messages hinting of why you cannot access getters or setters due to scoping. |
I agree with the above users, this is standard for most other languages. The argument against it does not hold weight and the implementation should be reconsidered. |
I actually was surprised you can not do this in TS... +1 to the issue.
Imo if you can read english it is self-explanatory what private(protected)/public mean here. Also if you are defining the accessors in a first place - you probably already know what they are for. P.s. About the error: "Getter and setter accessors do not agree in visibility" - well: that's exactly what I want them to do |
Here are two use cases where this would be handy: Backbone.js, to avoid ugly class Whatever {
public get rotation(): number {
return this.get('rotation');
}
private set rotation(rotation: number) {
this.set('rotation', rotation);
}
} Properties that subclasses can modify: class ExtendMe {
public get someProperty(): string {
// some stuff
}
protected set someProperty(prop: string) {
// some stuff
}
} |
I would definitely like to see this, as it has been bugging me since I started using TypeScript. |
I agree with the people requesting this feature. I just tried having a public get() method and was surprised to see that my set and get must agree on visibility. |
I've been reading you guys claiming it is too complex. Remembering the "C++ way" why it is different from:
I can see a lot of use cases for this, that's why I'm here right now. Basically, we want a method to be used each time we modify the attribute together with the syntactic sugar that makes it seems we're just assigning a value, instead than calling a method. |
"It's too hard" should never be a reason to not implement an incredibly useful feature. |
Adding my +1. It seems very odd to me that in a language that is so completely in so many regards you can't do this. |
+1 |
TypeScript is great even after C#, but this pointless limitation irritating me quite often. Please reopen it. |
Is this really this complex comparing to things like |
Bump. Everyone wants this feature. Shouldn't the TypesScript community get to decide? |
The complexity comes into play with the interaction of other language features. Unfortunately I can't find it, but IIRC RyanCavanaugh gave an example where this feature could allow you to set up and then violate invariants via inheritance using a class expression. Just because it's easy to write a certain declaration, that doesn't mean it'll always be easy to reason about how it affects things. The question is which problems will a given feature solve, and which will it create. The former is easy to answer, the latter can surprisingly hard to answer. Unfortunately the TS team sometimes seems to respond with "OMG complexity" instead of illustrating the problem. (To be fair, the more time they spend answering query x for the nth time, the less time they have to develop.) I don't 100% agree with the notion of "shouldn't the community get to decide", because if there is consensus among the experts actually developing the language, that should tell you something. But I think with something as requested as this, a thoughtful explanation from the team on what the trade-off is and why they're against it isn't too much to ask for. And personally, I think the trade-off is 1000% worth it in this case. But if I can't be bothered to spec it out and make it work, I suppose I don't have too much right to complain. |
Would this be a good time to revisit this issue? The I often choose not to make the property It would be great if there was some syntactic sugar to do this for you. Something like: // Option 1: C# style
public name: string { get; private set; }
// Option 2: Swift style
private(set) name: string
// Option 3: Swift struct-style
public readonly name: string
mutating changeName(name: string) {
this.name = name
}
// Option 4: New keyword
public frozen name1: string
public readonly name2: string I like option 2, imo it would fit well into the TypeScript language. With option 3, you can only change readonly fields in functions that are marked as With option 4, |
For reference, @yvbeek's ideas on more flexible modifiers is better suited for the discussion over in #37487. This issues is specifically for getters and setters, and has a super high number of upvotes! I think it would be useful to give getters and setters differing access modifiers (and we can update the existing set of modifiers over in #37487 if the TypeScript team ever decides it to be an acceptable thing to move forward with) |
@snarfblam Not to clog this thread with an irrelevant comment, but I think you have revealed a core principle for the way government should operate. |
Not having this feature is a real pain for me and (among other things) made me switch from TS/NodeJS to something more... type safe. It's all fine and dandy, but when you're working on a complex project with a a lot of data structures (deeply nested many times) and you cannot model the data properly, you get the feeling that this is not a language "for big boys". In my particular case, I want the property to be readonly, but modifiable from the inside... and also serialized to JSON. Too many hoops to jump through. |
This feature might follow the same path as optional chaining. People will ask for this feature for years and then finally it will be added to the language, because it is practical and other languages offer the same feature. Otherwise I hope that some implementation will become part of EcmaScript and then make its way to TypeScript. |
I recently just switched to typescript and I've been loving the language. I'm really bummed out that this practical feature that exists in other languages hasn't been implemented here. Please do reconsider adding it in a future release regardless of how much complexity you think it might add to the language. |
I achieved something similar to c# with getters that way: export class I18nService {
private static ref: I18nService;
public static get instance(): I18nService {
if (!I18nService.ref) {
I18nService.ref = new I18nService();
}
return I18nService.ref;
}
} |
Type errors like the following are easy to understand and are not complicated:
or
etc, and similar with That's honestly not complicated. |
BTW, I stumbled over this problem too, and I am using this kind of "hack" in the meantime: // somewhere.ts
declare global {
type Writable<T> = { -readonly [P in keyof T]: T[P]; }
}
// example.ts
class Example {
public readonly prop: number;
public doSomething(n: number): void {
(this as Writable<this>).prop = n;
}
} Technically, this could be used everywhere, but using this workaround should be restricted to code inside class methods. |
I've played with this idea myself, but the problem is you have no way to distinguish between "public read-only" and truly read-only properties. This makes it not very suitable as a general-purpose solution. |
There's a Playground build for PR 42425 available for trying out and I'd appreciate any feedback on how it meets folks' needs here. Thanks! See comments in #42425 on how this works. |
This is nice. I hope it's a step toward asymmetric accessibility of data properties. |
This is great to see. Apologies my prior comment was directed to the disnissive way this issue was summarily closed by the former team manager years ago. @RyanCavanaugh I'm glad to see typescript gaining more parity with languages like C#. Great work. |
I am sorry but this is a rubbish reason. Typescript includes features like private constructors, yet you say that the complexity of different getters/setters is too high and steepens the learning curve. Like somebody mentioned here, |
You might want to consider reading more than the first few comments. The one you quoted was written over 5 years ago, several months before even the addition of private constructors (TypeScript 2.0) you're talking about. |
@RiftLurker I know that, but that does not change the point. I did read the thread, and I do know the changes are in 4.3. |
Could it be possible to implement different access modifier for getter and setter? This way the setter could be for example private/protected and the getter public. In some cases this in really useful when the value shall be read-only.
Example:
The text was updated successfully, but these errors were encountered: