Skip to content
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

Provide a modifier to allow classes to protect property state while allowing mutations/changes within the class #39461

Closed
5 tasks done
Jonathan002 opened this issue Jul 7, 2020 · 3 comments
Labels
Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript

Comments

@Jonathan002
Copy link

Jonathan002 commented Jul 7, 2020

Search Terms

"modify readonly for internal classes" - leads to "as any" hack (Goal is to create convinience)
"protected" search - can only be read by derived classes

Suggestion

I find myself looking to complex solutions or longer code to protect state. I'm hoping typescript can be used to reduce code and protect state away from runtime through a public readonly modifier that has write access for internal methods of a class.

Use Cases

I'm hoping to save runtime through typing state protection and avoid extra code such as getters and frameworks that use runtime to manage state.

Examples

class Dog {
    readonlyPublic asleep = true
   wake() {
       // console.trace();
       this.asleep = false; // will not throw an error as expected with "readonly"
   }
}

const dogA = new Dog();
dogA.asleep = false // Error this is readonlyPublic (forcing us to use the dogA.wake() method to update state)
const isDogAsleep = dogA.asleep // but we can read it publicly without an error..
console.log(isDogAsleep) // => true

-------- update state
dogA.wake();
console.log(dogA.asleep) // => false;

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, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
@j-oliveras
Copy link
Contributor

If I understand correctly, you can already do it with private class fields: #30829, ECMAScript Private Fields on Handbook.

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Jul 7, 2020

Right, you could write

class Dog {
    get asleep { return this.#asleep; }
    #asleep = true;
    wake() {
        this.#asleep = false;
    }
}

and if you're okay with ordinary properties:

class Dog {
    get asleep { return this._asleep; }
    private _asleep = true;
    wake() {
        this._asleep = false;
    }
}

@DanielRosenwasser DanielRosenwasser added Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript labels Jul 7, 2020
@Jonathan002
Copy link
Author

Jonathan002 commented Jul 19, 2020

@DanielRosenwasser @j-oliveras Thanks for the comment. I don't think you had addressed my use cases with your comment though, and I hope the 3 use cases below outlines them more clearly.

I have mentioned getters in my opening comment and think you misunderstand my intent as I do not want to use them at all. The use case as mentioned is to avoid extra code such as getters. You are using them in both your examples. It's small so you will only notice one line of code. But when you have a lot of properties securing ways to read my classes properties through getters becomes redundant. I have more lines of code to read through and the file becomes very large if newline characters with spacing between braces is a requirement in the lint.

The second use case is runtime. It's small, but you skip the work of one extra function call that will return the reference of the value you are trying to get through a getter. I know it's very very small, but this difference might mean something if there were lots of getters pointing to values needed to do math in an animation loop to keep at higher frame rates in a browser.

The third use case is file size. If Typescript had acted as my getters instead of me relying on javascript to be my getters to protect my properties from being modified externally, we can prevent:

get asleep { return this._asleep; } from being outputted at all.

Javascript Output:

class Dog {
    constructor() {
        this._asleep = true;
    }
    wake() {
        this._asleep = false;
    }
}

vs

class Dog {
    constructor() {
        this._asleep = true;
    }
    get asleep() { return this._asleep; } // ~ 40ish kb
    wake() {
        this._asleep = false;
    }
}

This example is small, but on large projects, this really helps reduce characters with scale.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants