Typescript 父类对子类的类型保护

标签 typescript inheritance types parent-child instanceof

我有一个抽象类 FooObject .只有两个 child 可以继承FooObject ,让我们调用他们 ThisFooObjectThatFooObject .

我在找 高性能 将 FooObject 变成任何 child 的方法。在这种情况下,这意味着不使用 instanceof - 其中performs <10% 的 bool 查找速度。

我调查的方式是这样的:

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 的 child ,这不是一种可扩展的方法 - 并且它需要 parent 以一种封装破坏的方式了解 child 。但在我的具体情况下,如果它避免我不得不做'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';

但除此之外,我对这种模式相当满意。当我尝试使用 时出现问题不止一个 child 。我开始遇到循环依赖问题,以及诸如 class extends value undefined is not a constructor or null 之类的错误。 .

我不知道如何进行这项工作。我是否只是在错误的树上吠叫,我应该以更简单的方式做到这一点?

最佳答案

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

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

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

现在我们可以定义抽象 FooObject类(class):
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
  }
}
  • TypeScript 不允许抽象静态属性,所以我不能说 abstract static typeId如果我的子类没有实现它,让编译器警告我。所以我们只需要记住自己做。
  • 我们将复制静态 typeId创建时分配给每个实例,以便实例可以访问 this.typeId而不是 this.constructor.typeId .我不确定这是否对性能有很大帮助,但是您可以忽略它并替换对 this.typeId 的引用。与 this.constructor.typeId随心所欲。
  • instanceOf方法是通用的并接受 FooObject构造函数,并比较 typeId当前对象的 typeId传入的构造函数。它是通用的这一事实帮助我们避免父类需要了解每个子类,并避免您在上面遇到的疯狂网络。

  • 好的,让我们创建那些具体的子类。我们必须记住设置静态 typeId到一个独特的值(value):
    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 .

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

    关于Typescript 父类对子类的类型保护,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46459538/

    相关文章:

    javascript - 如何在 Ionic 中将文本转换为 html 对象

    angular - 禁用选定的下拉选项 Angular 2?

    python - 如何使 python 对象可通过 numpy.array() 调用

    C# 继承保护方法实现接口(interface)

    Typescript 接口(interface)方法 Vs 抽象方法语法差异

    java - Java中的单例和继承

    c# - 输入来自 InvalidCastException 的数据

    go - 与 Go 中文字的类型推断混淆

    java - 用于 Java 中看起来很奇怪的显式类型参数声明语法

    typescript - 使用保留关键字作为 typescript 界面中的属性