Typescript: Different access modifier for getter and setter

Created on 21 Apr 2015  ·  40Comments  ·  Source: microsoft/TypeScript

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:

class MyClass {
    private _myProp: any;
    private set myProp(value: any) {
        this._myProp = value;
    }
    public get myProp(): any {
        return this._myProp;
    }
}
Declined Suggestion Too Complex

Most helpful comment

Anyway adding my +1 for this, in case it is considered again in future...

All 40 comments

12

As #12 is closed with #6532 now it seems that this issue is out of scope.
Do you now have any plans on implementing different access modifiers?
Because readonly is not enough to solve what's described here as a property might be actually writable inside a class.

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 updatedAt actually can have a setter but it should not be accessable outside of Person. Moreover this setter contains complex logic and a simple readonly or an absence of a setter is not enough.

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 C#, I think it would be usefull to allow different visibility at compile time, though this contracts will be lost in runtime.

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.
There shouldn't bee any increased learning curve with this

private get x() { ... }
public set x(value) { ... }

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 .get() and .set() calls:

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:

private _myAttribute: string;
get myAttribute(): string {...}
setMyAttribute(value: string) {...}

I can see a lot of use cases for this, that's why I'm here right now.
What if I want myAttribute to be publicly accessible but allow only to be modified inside its class?
What if I want to add some custom logic each time the attribute is modified?
It can be a business rule, it can be just a logging to understand why it has been assigned a specific value, together with a breakpoint condition for debugging purposes, etc...

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.
C# has the concept of properties for this and I thought TS inherited that concept, but not allowing different accessibilities is a big limitations for people that think like me and make us fall back to the "C++ style".

"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.

Surprised to find that this issue is marked as closed. This feature is DESIRED by the community. Please reopen.
+1

12 doesn't address this issue, so this issue should not be closed with a comment that refers to that issue.

IMHO this is a nice feature but as fair as I understand it's just the situation "well I do NOT wan't to write everytime _property instead I want to use private set property"

If there is choice either adding huge complexity to the TypeScript core code itself (remember, more unnecessary complexity - less speed of developing things, more time to debug, write tests, in the end - less frequent releases) but reducing writing 1 symbol OR writing that 1 symbol and deal with it well... I would rather write 1 symbol one more time every time. In the end, I'm no contributor to TS core and I definitely don't want to deal myself with possible complexity of this sugar.

It's not a dealbreaker guys. Also it takes courage and wisdom to spot request as too complex to implement (with outcome increasing overall code complexity), so hats off to you guys developing TypeScript and thank you for your work!

(p.s. came here to learn why not - in the end changed my mind and will deal with this._property = ...
and will be still happy)

@idchlife The set property goal isn't to save developers one underscore when assigning value. You also might want to add some logic in the set process.

Please reopen this issue. This is clearly a highly desired feature.

+1 I would love to see this reconsidered.

Very strange your rationale is "this would make things confusing" when many other languages already do this.

Since all the 👍s are not doing much I'd just spam with one more 👍 comment.

This would be syntactic sugar which helps make things more readable and quick to implement. Lots of languages have syntactic sugar including Typescript. There doesn't seem to be any real good reasoning not to do it and too complex is never a good reason not to do something, it's more of a lazy cop-out. Solving problems that are too complex is one of the reasons most people are drawn to software engineering. So take this complex problem and solve it in a nice not so complex way. Will it require refactoring and risky changes? Maybe but that is how things get done.

Still desirable. Other languages have no trouble supporting different visibility for get- and set- functionality. To add injury to insult, Typescript is made by Microsoft, just like C#, but the latter fully supports it:

public string myPropertyWithLimitedAccess { get; private set; }   

Look at that. Beautifully readable, and totally nothing to complex.

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/auto-implemented-properties

There is little reason not to support:

private _myProperty: string;

public get myProperty(): string { return this._myProperty; }
private set myProperty(value: string) { this._myProperty = value; }

Bonus points in weird design decisions, because Typescript was primarily made for .NET developers to get more accustomed to working with the browser front-end.

+1
Typescript would definitely benefit from this functionality!

TypeScript is great even after C#, but this pointless limitation irritating me quite often. Please reopen it.

would increase the complexity of the language

Is this really this complex comparing to things like readonly [P in keyof T]: T[P]?

Bump. Everyone wants this feature. Shouldn't the TypesScript community get to decide?

would increase the complexity of the language

Is this really this complex comparing to things like readonly [P in keyof T]: T[P]?

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 readonly modifier is great, but there are many cases where you want to be able to change the value inside of the class and still have everything be read-only on the outside.

I often choose not to make the property readonly because I don't like the noise that a private backing field + the properties add.

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 mutating

With option 4, frozen can only be set in the constructor, readonly can be set inside of this class, not by any external classes or classes inheriting from this class.

For reference, @yvbeek's ideas on more flexible modifiers is better suited for the discussion over in https://github.com/microsoft/TypeScript/issues/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)

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.

@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:

Property 'foo' is writable only in protected scope within class 'Blah' and its subclasses.

or

Property 'foo' is readable only in protected scope within class 'Blah' and its subclasses.

etc, and similar with private.

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.

BTW, I stumbled over this problem too, and I am using this kind of "hack" in the meantime:

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zhuravlikjb picture zhuravlikjb  ·  3Comments

Antony-Jones picture Antony-Jones  ·  3Comments

siddjain picture siddjain  ·  3Comments

seanzer picture seanzer  ·  3Comments

bgrieder picture bgrieder  ·  3Comments