如何在基类方法中调用子类方法?

How do you call a subclass method in the baseclass method?

很抱歉,如果这个问题如此令人困惑,我将尝试尽可能简单。

TLDR; 如何在 Typescript baseclass 中调用必须在子class?

我有许多不同的 Typescript 子class需要不同的代码来做同样的事情:写入文件。

90% 的基本代码是相同的,每个子class.

有一些方法的具体实现

所以我采用了相同的代码并将其放在基 class 中,但是调用的方法必须在子 class 上实现并且最好是私有的。

这是一个例子:

abstract class BaseClass {
  constructor() { }

  SetOptions() {
    // Set the options
    // Return the options
  }

  GetOptions() {
    // Get the options
    this.WriteStuff();
  }

  private WriteStuff(arg1: Item, arg2: number) {}
}

class SubClass1 extends BaseClass {
  private WriteStuff(arg1: SubClass1Item, number: number) {
    // Write stuff in the SubClass1 way
  }
}

class SubClass2 extends BaseClass {
  private WriteStuff(arg1: SubClass2Item, number: number) {
    // Write stuff the SubClass2 way
  }
}

SubClass1Item 和 SubClass2Item 实现了 Item 接口。

所有子classes 将对 SetOptions 和 GetOptions 使用相同的代码,但 WriteStuff 将在每个子class 中具有个性化代码并且需要是私有的,因此不会被其他人调用开发者意外。

Typescript 说我不能这样做,因为“类型有单独的私有声明 属性 'WriteStuff'”。 Typescript 不允许我不在 BaseClass 中声明方法,因为我正在调用一个不存在但将存在于 subclass.

中的方法

这可能吗?

私有与受保护

private 方法只能在 class 本身中访问,不能在任何后代中访问。你不能有一个 private abstract 方法,因为这是一个内在的矛盾——如果后代需要实现它,那么它就不是 private.

您想使用 protected 修饰符。每 the docs:

The protected modifier acts much like the private modifier with the exception that members declared protected can also be accessed within deriving classes.

您想在 BaseClass 中声明所有具体化必须实现 WriteStuff() 方法。这是一个 abstract 方法,意味着它在 BaseClass 中没有实现并且 必须 被覆盖。

在你的后代中,WriteStuff的实现必须是protected而不是private

代码

abstract class BaseClass {
  /* .... */
  protected abstract WriteStuff(arg1: Item, arg2: number): void;
}
class SubClass1 extends BaseClass {
  protected WriteStuff(arg1: SubClass1Item, number: number) {
    // Write stuff in the SubClass1 way
  }
}

Playground Link

arg1 类型

您的 classes 并不都共享相同的签名,因为它们每个都具有不同的第一个参数 arg1 传递给 WriteStuff 的类型。您很可能希望根据 arg1 或其他值的类型使用 generic class

如果 SubClass1ItemSubClass2Item 不扩展 Item 那么你绝对 需要 一个通用的 class 因为实现subclasses 中的 WriteStuff 不能分配给 base。

如果他们扩展Item,那么您将不会在当前设置中遇到打字稿错误。但是,当您从 BaseClass 中的 GetOptions() 调用 this.WriteStuff 时,可能会出现运行时错误。 BaseClass 如何知道它拥有的 Item 是否可分配给 SubClass1SubClass2 中预期的 Item 子类型?这是泛型可以提供帮助的地方。

我们使 BaseClass 成为基于变量 ItemType 的泛型。我们可以要求所有 ItemType 变量扩展一个共享基 Item,但我们不必这样做。

子class本身不是通用的class。他们将使用他们需要的特定 ItemType 扩展 BaseClass 版本。

代码

abstract class BaseClass<ItemType extends Item> {
  /* ... */
  
  GetOptions(item: ItemType) {
    // Get the options
    this.WriteStuff(item, 5);
  }

  protected abstract WriteStuff(arg1: ItemType, arg2: number): void;
}
class SubClass1 extends BaseClass<SubClass1Item> {
  protected WriteStuff(arg1: SubClass1Item, number: number) {
    // Write stuff in the SubClass1 way
  }
}

Playground Link

JavaScript 最近支持 private field declarations including private instance methods。在那里,一种可能的方法是为基础 class' 超级调用提供 sub class 特定实现 ...

class BaseClass {

  // default implementation of private instance method.
  #writeStuff = () => void 0;

  constructor(writeStuff) {
    // at instantiation time, if appropriate, ...
    if (typeof writeStuff === 'function') {
    
      // ... overwrite the default implementation.
      this.#writeStuff = writeStuff;
    }
  }
  
  // prototypal method ...
  getOptions(...args) {
  
    // ... with access to a private instance method.
    this.#writeStuff(...args);
  }
  setOptions() {/* ... */}
}

class SubClass1 extends BaseClass {
  constructor() {

    const writeStuff = (...args) =>
      // Write stuff in the SubClass1 way
      console.log(`SubClass1 [...args] : ${ [...args] }`);

    super(writeStuff);

    // do other things
  }
}
class SubClass2 extends BaseClass {
  constructor() {

    const writeStuff = (...args) =>
      // Write stuff in the SubClass2 way
      console.log(`SubClass2 [...args] : ${ [...args] }`);

    super(writeStuff);

    // do other things
  }
}

const objA = new SubClass1;
const objB = new SubClass2;

objA.getOptions('foo', 'bar');
objB.getOptions('baz', 'biz');
.as-console-wrapper { min-height: 100%!important; top: 0; }