【问题标题】:Typescript Parent Class type guard to Child ClassesTypescript Parent Class 对子类的类型保护
【发布时间】:2017-09-28 01:25:31
【问题描述】:

我有一个抽象类FooObject。只有两个孩子继承了FooObject,我们称他们为ThisFooObject 和ThatFooObject。

我正在寻找一种 高效 方法来将 FooObject 转换为它的任何子对象。在这种情况下,这意味着不使用 instanceof - performs 以

我调查的方式是这样的:

FooObject.ts

import ThisObject from "./ThisObject";
import ThatObject from "./ThatObject";

abstract class FooObject {

  isThisObject() : this is ThisFooObject {
    // Either check for ThisFooObject properties, or override this
    // in the ThisFooObject class to always return true.
    return duckTypingTest();
  }

  // ditto here
  isThatObject() : this is ThatFooObject {}
}

我知道这不漂亮。如果我有更多 FooObject 的孩子,这不是一种可扩展的方法——它需要父母以一种打破封装的方式了解孩子。但在我的具体情况下,如果它避免我不得不做'instanceof',我愿意忍受这个。

从好的方面来说,当我有一个 FooObject 的引用,并且当它是一个 ThatFooObject 时我想要一些东西时,我可以执行以下操作:

fooObjects.forEach( foo => {
  // Foo currently typed as a FooObject
  if(foo.isThatObject()){
    // Foo now typed as a ThatFooObject
    foo.magic+=10
  }
});

这一切都有效 - 有一点需要注意 - 我必须 be careful 才能使导入顺序正确。 IE,在我的main.ts 入口点,做类似的事情

import 'ThatFooObject';
import 'FooObject';

但除此之外,我对这种模式相当满意。当我对多个孩子进行尝试时,问题就来了。我开始遇到循环依赖项的问题,以及像class extends value undefined is not a constructor or null 这样的错误。

我不知道如何进行这项工作。我是不是找错了树,我应该用更简单的方法来做这件事吗?

【问题讨论】:

  • 如果您从进口订单中收到类似的错误,那么您的装载机不符合规范。
  • 无法重现此问题,无论我以何种顺序导入三个文件,始终以正确的顺序导入。也许您可以提供一个导致循环依赖或错误导入顺序的完整示例?一种丑陋的解决方案(对于您实际遇到的任何问题)可能是将导入语句移动到 FooObject.ts 的底部,以强制在创建 FooObject 类之后导入这些其他文件。

标签: typescript inheritance types parent-child instanceof


【解决方案1】:

这样的事情怎么样,不需要类相互了解:

计划是为每个具体的构造函数提供一个名为typeId 的字符串属性,我们将使用它来区分子类。这是FooObject 构造函数的形状:

interface FooObjectConstructor<T extends FooObject> {
  new(...args: any[]): T
  typeId: string;
}

现在我们可以定义抽象的FooObject 类:

abstract class FooObject {

  // note 1
  // abstract static readonly typeId: string;  

  // note 2
  readonly typeId = (this.constructor as FooObjectConstructor<this>).typeId; 

  // note 3
  instanceOf<T extends FooObject>(ctor: FooObjectConstructor<T>): this is T {
    return this.typeId === ctor.typeId
  }
}
  1. TypeScript 不允许抽象静态属性,所以我不能说 abstract static typeId 如果我的子类没有实现它,编译器会警告我。所以我们只需要记住自己做。

  2. 我们将在创建时将静态typeId复制到每个实例,以便实例可以访问this.typeId而不是this.constructor.typeId。我不确定这是否对性能有很大帮助,但如果需要,您可以将其省略,并将对 this.typeId 的引用替换为 this.constructor.typeId。

  3. instanceOf 方法是泛型的,接受FooObject 构造函数,并将当前对象的typeId 与传入构造函数的typeId 进行比较。它是通用的这一事实有助于我们避免父类需要了解每个子类,并避免您在上面遇到的疯狂网络。

好的,让我们创建这些具体的子类。我们必须记住将静态 typeId 设置为唯一值:

class ThisObject extends FooObject {
  static readonly typeId = "ThisObject" // don't forget this
  thisMethod() {
    console.log("This!")
  }      
}

class ThatObject extends FooObject {
  static readonly typeId = "ThatObject" // don't forget this
  thatMethod() {
    console.log("That!")
  }
}

好的,是时候测试一下了!

declare const someFooObject: FooObject;

if (someFooObject.instanceOf(ThisObject)) {
  someFooObject.thisMethod();
} else if (someFooObject.instanceOf(ThatObject)) {
  someFooObject.thatMethod();
} else {
  // some other type of FooObject, I guess
  console.log("I wasn't expecting that");
}

上面的所有代码都不会对我产生编译警告,它是works for me at runtime。

希望有所帮助;祝你好运!

【讨论】:

    【解决方案2】:

    好的,我找到了一个解决方案 - 但不是很好。

    看看我必须在 main.ts 中通过在父类之前导入子类来强制正确的导入顺序:

    main.ts

    import "./ThisFooObject"; // <-- forces correct order.
    import FooObject from "./FooObject";
    

    我必须加倍努力,对所有孩子都这样做,对所有其他孩子。因为我只有两个,所以这不是一个大问题,但是随着子类的增多,它会成倍地扩展。例如:

    ThisFooObject.ts

    import "./ThatFooObject" <-- Forces correct order despite not being used
    import "./FooObject"
    

    ThatFooObject.ts

    import "./ThisFooObject" <-- Forces correct order despite not being used
    import "./FooObject"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-11
      • 2020-02-01
      • 2021-01-22
      • 2019-01-04
      • 1970-01-01
      • 2019-02-13
      • 2021-12-10
      • 2018-12-03
      相关资源
      最近更新 更多