I am new to Typescript, and I tried to played around a little with TypeScript in this playground. I noticed that in TypeScript, a protected member in base class can be overridden by public members:
class Base {
protected name: string = '!'
}
class Derived extends Base{
public name: string = '?'
}
On the one hand, this makes sense to me since the Liskov Substitution Principle still holds: base class has stricter requirements than derived class. But on the other hand, I noticed that private member cannot be overridden by protected or public one, which seems inconsistent to me:
class Base {
private name: string = '!'
}
class Derived extends Base{
public name: string = '?' // ERROR!
}
Thus I wonder:
Is my observation an intended behavior or a bug in Typescript?
If it's intended, why this inconsistency exists? Why not TypeScript requires all overriding members having the same accessibility as members in base class? Or allowing all derived members with higher accessibility overriding members in base class?
This is the intended behavior.
You can make a protected
field public
because protected
allows a derived class to read and write a field. The derived class can choose to use its ability to read and write the field to allow others to read and write the field. There's no point making you write something like this:
class Foo {
protected someField;
}
class Bar extends Foo {
public get someFieldButPublic() {
return this.someField;
}
public set someFieldButPublic(value) {
this.someField = value;
}
}
if all you wanted to do was make someField
public.
You can't make a private
field protected
or public
because you don't have read or write access to that field. It's private
; if the base class wanted you to have access to the field, they would have made it protected
, after all.
This is intended behavior.
TypeScript compiles to JavaScript. Because of this, using access modifiers in your code has absolutely no effect on the output. The only thing that access modifiers do is let the compiler yell at you if you use something that you shouldn't be accessing.
Example: Both of these classes compile to the exact same code. Playground (ignore the class name)
// prop is private
class Test {
private prop: string;
constructor() {
this.prop = "str"
}
}
// prop is public
class Test {
public prop: string;
constructor() {
this.prop = "str"
}
}
In languages like C#, private properties are truly private, and thus it is possible to inherit from a class with a private name property while exposing a name property. Inherited methods accessing this.name
will access the name
property in the base class, while methods in your class accessing this.name
will use the property in the inheriting class.
Let's look at the emitted JavaScript for your example.
var __extends; // omitted for brevity
var Base = (function () {
function Base() {
this.name = '!';
}
return Base;
}());
var Derived = (function (_super) {
__extends(Derived, _super);
function Derived() {
var _this = _super.apply(this, arguments) || this;
_this.name = '?';
return _this;
}
return Derived;
}(Base));
As you can see, what happens here is the Base
class assigns !
to this.name
, Derived
then changes the supposedly private name
property of Base
to ?
. Obviously, this could result in some incredibly confusing bugs when methods in the Base
class refer to this.name
and get the unexpected value assigned by the Derived
class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With