为什么受保护的成员可以被TypeScript中的公共成员覆盖?

时间:2017-03-29 04:40:15

标签: javascript class oop inheritance typescript

我是Typescript的新手,我试图在这个playground中使用TypeScript玩一下。我注意到在TypeScript中,基类中的受保护成员可以被公共成员覆盖:

class Base {
    protected name: string = '!'
}

class Derived extends Base{
    public name: string = '?'
}

一方面,这对我来说很有意义,因为Liskov替换原则仍然存在:基类比派生类有更严格的要求。但另一方面,我注意到私人成员不能被受保护或公开成员覆盖,这似乎与我不一致:

class Base {
    private name: string = '!'
}

class Derived extends Base{
    public name: string = '?'  // ERROR!
}

因此我想知道:

  1. 我的观察是针对打字稿的预期行为还是错误?

  2. 如果有意,为什么存在这种不一致?为什么TypeScript要求所有覆盖成员具有与基类成员相同的可访问性?或者允许所有具有更高可访问性的派生成员覆盖基类中的成员?

3 个答案:

答案 0 :(得分:5)

这是预期的行为。

您可以创建protected字段public,因为protected允许派生类读取和写入字段。派生类可以选择使用其读取和写入字段的能力,以允许其他人读取和写入字段。没有必要让你写下这样的东西:

class Foo {
  protected someField;
}

class Bar extends Foo {
  public get someFieldButPublic() {
    return this.someField;
  }
  public set someFieldButPublic(value) {
    this.someField = value;
  }
}

如果你想做的就是公开someField

您无法建立private字段protectedpublic,因为您没有该字段的读取或写入权限。它是private;如果基类希望您有权访问该字段,那么毕竟它们会成为protected

答案 1 :(得分:0)

javascript没有这个功能,因为在java中调用它是一个字段rewrite.let看起来是java中的代码片段:

class Base {
    protected String name = "!";

    public String bar() {
        return name;
    }
}

class Derived extends Base {
    public String name = "?";

    public String foo() {
        return name;
    }
}

在java中持有一种派生实例it时。然后it.foo()返回"?"并且it.bar()返回"!"。但是为什么两个方法都在javascript中返回"?",因为javascript行为发生在运行时并绑定{{ 1}}输入到Derived in Base,所以你不能在javascript中重写字段。你还记得this吗?在Derived类中做这样的事情,如果在Derived实例上调用bar方法,它实际上是绑定的派生类型到Base类。你可以禁止做这样的事情是让字段在Base中保密,让编译器只在打字稿中告诉你这个错误。

答案 2 :(得分:0)

这是预期的行为。

TypeScript编译为JavaScript。因此,在代码中使用访问修饰符对输出完全没有影响。访问修饰符唯一能做的就是让编译器在你使用你不应该访问的东西时大喊大叫。

示例:这两个类都编译为完全相同的代码。 Playground(忽略班级名称)

// prop is private
class Test {
    private prop: string;
    constructor() {
        this.prop = "str"
    }
}

// prop is public
class Test {
    public prop: string;
    constructor() {
        this.prop = "str"
    }
}

在像C#这样的语言中,私有属性是真正的私有属性,因此可以在暴露名称属性的同时从具有私有名称属性的类继承。访问this.name的继承方法将访问基类中的name属性,而访问this.name的类中的方法将使用继承类中的属性。

让我们看一下您的示例所发出的JavaScript。

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));

正如您所看到的,此处发生的是Base类将!分配给this.nameDerived然后更改name的所谓私有Base属性{1}}到?。显然,当Base类中的方法引用this.name并获取Derived类指定的意外值时,这可能会导致一些令人难以置信的混淆错误。