Repository navigation
Suggestion: 'protected' modifier #1
Description
General proposal
protected modifier acts the same as private in terms of code generation and assignability, except that it is possible to access a protected member in any subclass of a class which declared the member. Basically, you can access protected members in the same situations where it would have been an error for you to redeclare a private member with the same name as a base class's member.
Examples
class Base {
protected myMember;
}
class Derived extends Base {
foo() { return this.myMember; } // OK
}
var x = new Derived();
console.log(x.myMember); // Error, cannot access
class Derived2 extends Base {
private myMember; // Error, cannot tighten visibility
public myMember; // Error, cannot widen visibility
}Open Questions
After the last design meeting, the following open questions remained:
Is 'sibling' property access allowed?
C# and other IL languages prohibit this pattern, but Java allows it:
class Base { protected x: string; }
class Derived extends Base {
foo(n: Base) {
// Not allowed: cannot reference protected member through base class reference
console.log(n.x);
}
}See these links for reasoning
http://blogs.msdn.com/b/ericlippert/archive/2008/03/28/why-can-t-i-access-a-protected-member-from-a-derived-class-part-two-why-can-i.aspx
http://stackoverflow.com/questions/1904782/whats-the-real-reason-for-preventing-protected-member-access
Are protected members subject to the "same-declaration" rule as private members for assignability/subtyping?
private members are considered equivalent for the purposes of assignability and subtyping if they are from the "same declaration". This isn't quite the rule you would want for protected members. Consider some classes:
class Widget {
protected inspector: WidgetInspector;
}
class SquareWidget extends Widget {
// Use a more-specific 'inspector'
protected inspector: SquareWidgetInspector;
}
class CircleWidget extends Widget {
// Initialize here
protected inspector: WidgetInspector = new WidgetInspector();
}
var w: Widget;
var c: CircleWidget;
w = c; // Allowed, or not?If we took the verbatim "same declaration" rule from private, this assignment would be disallowed because w.inspector and c.inspector come from different declarations. It's not reasonable to have this assignment be disallowed.
However, if we do not use the "same declaration" rule, then a SquareWidget would be assignable to a CirceWidget even if they both removed their extends clauses. This is not surprising if you're used to thinking about things structurally, but since many people seem to like the higher specificity of private in terms of preventing assignability between structurally-equivalent types, this behavior might not be desirable.
A proposed rule was that we could have a notion of a "parent" declaration when a derived class's property overrides a base class property. This seems tractable for classes, but interfaces can extend multiple classes, and we would need to define what exactly that means. A degenerate example:
interface WatWidget1 extends Widget, CircleWidget { }
interface WatWidget2 extends Widget, SquareWidget { }
var ww1: WatWidget1;
var ww2: WatWidget2;
ww1 = new Widget(); // Allowed or not?
ww1 = new CircleWidget(); // Allowed or not?
ww2 = new Widget(); // Allowed or not?
ww2 = new CircleWidget(); // Allowed or not?
ww1 = ww2;
ww2 = ww1;Can public properties be assigned to protected fields?
class Point1 { x: number }
var p1: Point1 = { x: 3 }; // Allowed
class Point2 { private x: number }
var p2: Point2 = { x: 3 }; // Disallowed
class Point3 { protected x: number }
var p: Point3 = { x: 3 }; // Allowed or not?This is sort of a yes-or-no thing tangentially related to the previous question.
Activity
25 remaining items
NoelAbrahams commentedon Sep 16, 2014
Ah, okay... so I learnt a few things today:
plus the actual evidence from Ryan, together makes for a better answer than the one involving ships 😃
Perhaps these should be committed to the design goals (as at present it says nothing about C# or breaking changes) in order to prevent discussions veering off in fanciful directions that are never going to be considered.
csnover commentedon Sep 16, 2014
I’m not a language designer so I could be way off base, but the problem I see with any statement about relating to languages like C#, Java, etc. is that those languages use nominal type systems, not structural type systems. As a result, I don’t think it is possible to design TypeScript private/protected properties by copying patterns that work in these other languages’ type systems, since those patterns don’t really seem to mesh with the design of TS. I feel like the questions raised by the OP verify this situation, but I would be interested to see what design approach could be taken that isn’t confusing or impossible that doesn’t conform to my current thoughts/feedback.
RyanCavanaugh commentedon Sep 16, 2014
The design goals now go up to 11 🎸
C# is not an explicit design target (see non-goal #1). Rather, Jonathan's statement was that our behavior for
privateis broadly in line with other languages (C++, Java, Python, PHP, Objective C, VB.Net, Swift, etc). In general, it is going to be better to use behavior that works the same as it does it other commonly-used languages, especially if the majority of languages agree on that behavior. It improves predictability for people new to the language, and it's usually the case that everyone is making the same decision for a good reason. That doesn't mean we're never going to deviate (putting types on the right-hand side of an identifier, for example), but those kind of choices should be made with clear reasoning.With that said, let's please talk about
protected😕NoelAbrahams commentedon Sep 16, 2014
Non-goal number 7:
sophiajt commentedon Sep 16, 2014
👍 to Noel Abrahams (@NoelAbrahams) 's suggestion for non-goal 7
ahejlsberg commentedon Sep 19, 2014
Closed in #688.
Merge pull request #1 from Microsoft/master
Merge pull request #1 from Microsoft/master
Tweak microsoft#1
Tweak microsoft#1
c1moore commentedon Jan 16, 2018
I realize this is probably already set in stone and I apologize for digging up a bones, but I wanted some clarifications on the decision that was made for "Is 'sibling' property access allowed?"
To me, it makes sense that siblings can use each other's protected methods if the protected method is defined in a common parent (super) class. In effect, I view protected methods as creating an interface (as opposed to defining an implementation detail) that can be viewed by ______ (the blank space is specific to the language, in TypeScript's case, it would be the inheritance chain).
In effect, this is how I think about it: The sub classes know that they have to implement the interface defined by the super class. Similarly, they know that all their siblings have to do the same in order to be an instance of the parent class. They can see the protected method from their parent, so it must exist on the siblings. Since these classes know this, it doesn't seem like its breaking encapsulation by allowing siblings to access protected methods. I place emphasis to indicate that the protected method is not optional and that it is a known fact by all siblings that it exists. This is different than private methods in which case the existence of the methods are unknown and therefore unusable outside the class.
I ask because I'm designing some classes using a composite pattern. One of the protected methods for the composite object is supposed to iterate over the objects it contains and return a composite value. The method is defined as protected in the super class and therefore known to exist by the composite object. Obviously I could easily make the method public, but following the principle of least privilege, it makes sense to make it protected since this method is only used within the inheritance chain.