【问题标题】:Instantiating an abstract-derived class by its type name in TypeScript通过 TypeScript 中的类型名称实例化抽象派生类
【发布时间】:2018-06-26 23:19:45
【问题描述】:

我正在使用 TypeScript 2.6 编写一些图形代码,但在继承和通用代码方面我遇到了一个小障碍:

abstract class Series { /**/ }

class LineSeries    extends Series { /**/ }
class BarSeries     extends Series { /**/ }
class ScatterSeries extends Series { /**/ }

class Chart {
  private seriesArray: Series[] = [];

  constructor(series: Series[]) {
    let seriesType: typeof Series = null;

    switch (series.SeriesType) {
      case SeriesType.LineSeries:
        seriesType = LineSeries;
        break;
      case SeriesType.BarSeries:
        seriesType = BarSeries;
        break;
      case SeriesType.ScatterSeries:
        seriesType = ScatterSeries;
        break;
      default:
        throw "Unsupported series type";
    }

    for (let i = 0; i < series.length; i++)
      this.SeriesArray.push(new seriesType());
  }
}

我希望上面的代码构造一个派生自Series 的新对象,其中最后一行的构造函数来自运行时分配的类型值。我遇到的问题是Series 是抽象的,所以我无法实例化它,但是我永远不会将值typeof Series 分配给series 变量。 (但 TypeScript 不知道这一点)。

我已经想到了一些解决方案,但没有一个是特别优雅的。一种是简单地从Series 中删除abstract 修饰符,并可选地放入一些代码以在实例化时抛出异常。另一种解决方法是将seriesType的定义改为:

let seriesType: (typeof LineSeries | typeof BarSeries | typeof ScatterSeries) = null;

但是每次添加新类型的系列时,我都需要维护此列表。我知道我可以做到,例如:

type DerivedSeries = (typeof LineSeries | typeof BarSeries | typeof ScatterSeries);

然后

let seriesType: DerivedSeries = null;

但这并没有太大的改进,如果有的话。

有没有更优雅的方法可以告诉 TypeScript seriesTypeSeries 类型,但也必须是可实例化的子类型?即表达式 seriesType = Series; 应该抛出编译时错误,而不是 new seriesType();

谢谢

【问题讨论】:

    标签: typescript inheritance abstract-class typescript2.6


    【解决方案1】:

    实例化派生自抽象类的类的最简单方法是使用new() 方法定义对象类型:

    abstract class AbstractClass { /**/ }
    
    class DerivedClassA extends AbstractClass { /**/ }
    class DerivedClassB extends AbstractClass { /**/ }
    
    const classCtors: Array<{ new(): AbstractClass }> = [
        DerivedClassA,
        DerivedClassB
    ];
    
    for (const ctor of classCtors) {
        const instance = new ctor();
    }
    

    【讨论】:

    • 其实把原问题代码中的let seriesType: typeof Series = null;换成let seriesType: {new(): Series} = null;就够了
    • 我真的不明白这里发生了什么,除了它似乎是一个匿名对象,其 ctor 返回一个 AbstractClass(或 Series)对象。在测试中,它可以工作,除了我在switch 中的声明,例如seriesType = LineSeries;,不起作用:Type 'typeof LineSeries' 不可分配给 type 'new () => Series'。 它还抱怨我在原始问题中遗漏的参数数量,但大概我需要在new() 中添加这些参数。
    • @artem - 我对使用原始问题代码犹豫不决,因为它有很多错误。例如,for 循环在类主体中浮动,并且开关正在对不存在的属性进行操作。我想它只是从几个不同的地方复制过来的。
    • @Ozzah - 是的,您必须将参数添加到 new() 方法(或使用 new(...args: any[]),尽管我不推荐它)。这只是一个普通的方法定义,但用于在对象上使用new。它允许您像使用任何其他方法一样定义参数和返回类型。如果您的类有静态方法,或者没有在抽象基类上定义的方法会使事情复杂化。
    • @Ozzah 错误 - Type 'typeof LineSeries' is not assignable to type 'new () =&gt; Series' - 如果 LineSeries 类的构造函数带有一些参数,则可能发生,因此它与没有任何参数的 constructor signature 不兼容。该匿名对象类型{new(....): Series} 必须为其new() 声明参数,以便它与所有派生类构造函数兼容。
    猜你喜欢
    • 2017-04-28
    • 2015-03-23
    • 2014-10-13
    • 2021-05-17
    • 2021-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多