JS Flow:流程中的继承是否破裂?

时间:2018-05-08 21:56:25

标签: javascript inheritance flowtype

考虑从类A扩展B类的情况。创建B类型的对象并调用A中定义的方法fooA,然后调用B中定义的方法fooB。

class A {
  fooA () {
    console.log('fooA called')
    return this
  }
}

class B extends A {
  fooB () {
    console.log('fooB called')
    return this
  }
}

new B().fooA().fooB()

运行时,代码会按预期记录以下内容

fooA called
fooB called

所以Javascript理解new B().fooA()是B类的对象。但是Flow给出了以下错误消息:

Cannot call new B().fooA().fooB because property fooB is missing in A

怎么办?我对一个解决方案感兴趣,我不需要更改父类A,因为它是在npm包中定义的。我可以改变B。

2 个答案:

答案 0 :(得分:1)

如果键入fooA方法作为返回this,则Flow会理解扩展A类的任何类也将从该方法返回自己的实例:

Try

class A {
  fooA (): this {
    console.log('fooA called')
    return this
  }
}

class B extends A {
  fooB () {
    console.log('fooB called')
    return this
  }
}

new B().fooA().fooB() // No error

由于您不想更改A类:另一种简单的方法是键入fooA类的B函数以返回实例B

Try

class A {
  fooA () {
    console.log('fooA called')
    return this
  }
}

class B extends A {
  fooB () {
    console.log('fooB called')
    return this
  }
  fooA: () => B; // Hey Flow, this actually returns a B
}

new B().fooA().fooB() // No error!

答案 1 :(得分:0)

函数fooA()返回“this”,它是“A”的一个实例。如您所知,A类不知道方法fooB存在。因此,Flow(正确地)指出A中缺少属性fooB。

知道实际上,A的这个特定实例也是B的实例,但Flow无法推断出这一点。你必须告诉它fooA()返回的A是使用强制转换的特定场景中的B实例。

将呼叫更改为(new B().fooA(): B).fooB()应解决Flow错误。

Javascript(sans Flow)并不关心这种语义。当你在fooA()返回的对象上调用“fooB”时,它只是在对象中寻找一个名为“fooB”的方法,它恰好存在,所以它有效,尽管它更容易破坏重构。流程很有用,因为它会强制您了解被抛出的类型,并在将来为这些问题提供编译时检查。