类型防护不适用于具有相同接口的子类

时间:2019-05-08 07:10:59

标签: typescript

得到错误的确切原因是什么

  

类型'never'不存在属性'descriminator'。

第67和70行。

See stackblitz here

type DescriminatorType = 'SubClassA' | 'SubClassB' | 'SubClassC'

使用静态类型保护函数定义一些抽象基类

export abstract class BaseClass {

  constructor(
    public descriminator: string,
  ) {
  }

  // tpye-guard function for SubClassA
  public static isSubClassA(o: BaseClass): o is SubClassA {
    return o instanceof SubClassA;

    // Or
    // return o.descriminator === 'SubClassA';
  }

  // tpye-guard function for SubClassB
  public static isSubClassB(o: BaseClass): o is SubClassB {
    return o instanceof SubClassB;

    // Or
    // return o.descriminator === 'SubClassA';
  }

  // tpye-guard function for SubClassC
  public static isSubClassC(o: BaseClass): o is SubClassC {
    return o instanceof SubClassC;

    // Or
    // return o.descriminator === 'SubClassA';
  }
}

定义一些子类。当前,其中两个具有相同的界面。

export class SubClassA extends BaseClass {
  constructor(
  ) {
    super('SubClassA');
  }
}

export class SubClassB extends BaseClass {
  constructor(
  ) {
    super('SubClassB');
  }
}

export class SubClassC extends BaseClass {

  constructor(
    public subClassProp1: number,
  ) {
    super('SubClassC');
  }
}

定义一些利用类型保护功能的程序逻辑:

export class OtherClass {
  data = [new SubClassA(), new SubClassA(), new SubClassB(), new SubClassC(123)]
  public doSomething(): string[] {

    return this.data.map(d => {
      if (BaseClass.isSubClassA(d)) {
        return d.descriminator;
      }
      else if (BaseClass.isSubClassB(d)) {
        return d.descriminator;   // Error: Property 'descriminator' does not exist on type 'never'.
      }
      else if (BaseClass.isSubClassC(d)) {
        return d.descriminator;   // Error: Property 'descriminator' does not exist on type 'never'.
      }
      else {
        return 'UNKNOWN TYPE';
      }
    })
  }
}

正如您在运行时在StackBlitz中看到的那样,一切正常。我正在获得预期的输出!

1 个答案:

答案 0 :(得分:3)

打字稿类型系统本质上是结构性的。如果两个类型分别声明为BaseClassSubClassA,但结构相同,则就类型系统而言,它们是完全相同的类型(至少在大多数实际情况下)

类型防护在else分支上工作,方法是从联合中取出与类型防护匹配的类型。由于SubClassBSubClassA具有相同的结构,因此也将其从联合中删除(本质上,它们是同一类型,但名称不同)

SubClassC之所以被删除,是因为类型保护也会取出匹配类型的子类型,并且由于SubClasssABaseClass是同一类型,因此SubClassC从技术上来说是说来,SubClassA

的子类型

如果没有其他类型需要处理,所有这些将导致else分支,而d将是never类型

在这种情况下,最简单的解决方案是将discriminator保留为BaseClass上的类型参数,这样,类在结构上会有所不同(使用此类型参数作为{{1 }},未使用的类型参数不计入结构):

discriminator