Class 在TypeScript中,如何防止对派生类调用方法?

Class 在TypeScript中,如何防止对派生类调用方法?,class,typescript,subclass,derived-class,Class,Typescript,Subclass,Derived Class,有三节课 // in external library, which I don't want to modify class ComponentBase { // I want calling this to be disallowed forceUpdate() {} } class ComponentBase_MyVersion extends ComponentBase { // I want subclasses to always call this, in

有三节课

// in external library, which I don't want to modify
class ComponentBase {
    // I want calling this to be disallowed
    forceUpdate() {}
}

class ComponentBase_MyVersion extends ComponentBase {
    // I want subclasses to always call this, instead of forceUpdate()
    Update() {}
}

class MyComponent extends ComponentBase_MyVersion {
    DoSomething() {
        // I want this to be disallowed
        this.forceUpdate();

        // forcing the subclass to call this instead
        this.Update();
    }
}
我如何才能做到这一点,只需更改
ComponentBase\u MyVersion

有没有办法“隐藏”基类成员


或者是一种覆盖定义的方法——比如C#中的“new”关键字——让我破坏方法定义,至少在尝试调用它时会出现警告?

我找到了一种似乎有效的方法——也就是说,当有人试图调用
forceUpdate()时会出现警告
在子类实例上

forceUpdate(_: ()=>"Do not call this. Call Update() instead.") {
    throw new Error("Do not call this. Call Update() instead.");
}
现在,当我编写
newmycomponent().forceUpdate()
时,我得到一个编译器错误,警告消息中包含一个说明,告诉我改用
Update()

编辑:显然,这只适用于基类,因为基类已具有此定义:

forceUpdate(callBack?: () => any): void;
相反,如果基本方法最初是在没有参数的情况下定义的(如在OP中),则上述解决方案不起作用


但是,如果您有一个像我这样的案例(其中有一个这样的可选属性,您可以缩小返回类型),它可以正常工作。(不确定此返回类型缩小是错误还是有意的)

通过用组合替换继承来封装实现

您可以通过在
forceUpdate
方法上添加
private
access修饰符来完成此操作。这将导致所有子类无法访问
forceUpdate
。但是,TypeScript不支持包访问修饰符,但可以通过用组合替换继承来实现这一点

class ComponentBase {
    forceUpdate() {
    }
}

class ComponentBase_MyVersion {
    // Replace inheritance with composition.
    private component: ComponentBase;

    Update() {
        this.component.forceUpdate();
    }
}

class MyComponent extends ComponentBase_MyVersion {
    DoSomething() {
        // Now subclass can't access forceUpdate method
        this.Update();
    }
}
使用
符号
以防止外部访问该方法。
如果不想用合成替换继承,可以使用
Symbol
定义方法。如果您的目标是
es5
,则必须配置
tsconfig.json
compilerOptions.lib
以包括
es2015.symbol
。由于每个
符号
都是唯一的,因此任何外部模块都无法获取该符号并访问该方法

// libs.ts
let forceUpdate = Symbol("forceUpdate");
export class ComponentBase {
    [forceUpdate]() {
    }
}

export default class ComponentBase_MyVersion extends ComponentBase {
    Update() {
        this[forceUpdate]();
    }
}


// test.ts
import ComponentBase_MyVersion from "./libs";
class MyComponent extends ComponentBase_MyVersion {
    DoSomething() {
        // Now subclass can't access the forceUpdate method.
        this.Update();
    }
}

OOP不允许您执行这种方法取消。您可以像建议的那样,在类上执行此函数,但出现异常,或者使用组合:

例1:

class ComponentBase {
    forceUpdate() {}
}

class ComponentBase_MyVersion extends ComponentBase {
    Update() {}
    forceUpdate() {
        throw new Error("Do not call this. Call Update() instead.");
    }
}

class MyComponent extends ComponentBase_MyVersion {
    DoSomething() {
        // wil raise an exception
        this.forceUpdate();
        this.Update();
    }
}
示例2(组成):


希望我能提供帮助。

当我这样做时,我得到一个编译错误(在TypeScript 2.1.5中):“类'ComponentBase\u MyVersion'错误地扩展了基类'ComponentBase'。属性'forceUpdate'在类型'ComponentBase\u MyVersion'中是私有的,但在类型'ComponentBase'中不是。”OP说,他们只想通过对
ComponentBase\u MyVersion
@Venryx进行更改来实现这一点,您可以用composite替换继承。将
ComponentBase\u MyVersion
方法委托给
ComponentBase
@Venryx我找到了解决您问题的另一个解决方案,当我误读您的问题时,我更新了我的asnwer&fix答案。@holi java有趣的解决方案!在我的例子中,它不可用,因为我没有(方便的)访问权限来修改
ComponentBase
类,但这对于只希望在一个类级别下可见的自定义基类很有帮助。(即,仅可由直接子类使用)要使用它,您需要在基类上更改
forceUpdate()
的签名,但您不想这样做。@Diullei哦,看起来您是对的,例如OP。但是,在我的实际用例中,它是有效的,因为real forceUpdate()方法接受一个类型为
()=>any
的(可选)参数。我的覆盖只是将需求缩小到
()=>“random string”
——这本身不会导致错误,但会在尝试正常使用函数时显示错误。因此,上面的解决方案在一般情况下不起作用,但在我的情况下起作用。(因此,对于其他观众来说,提到这一点可能仍然很有用)很好,对于一般解决方案,按照您最初的问题,合成方法似乎是答案,但我很高兴您找到了一种处理代码的方法:)
class ComponentBase {
    forceUpdate() {}
}

class ComponentBase_MyVersion {
    private _component: ComponentBase = ...;
    Update() {}
    // expose _component desired members ...
}

class MyComponent extends ComponentBase_MyVersion {
    DoSomething() {
        // compilation error
        this.forceUpdate();
        this.Update();
    }
}