【问题标题】:JS Flow: Is inheritance in Flow broken?JS Flow:Flow 中的继承是否被破坏?
【发布时间】:2018-05-10 00:20:16
【问题描述】:

假设您有一个从类 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。

【问题讨论】:

  • 我不使用 flow,但我倾向于认为它的行为正确。考虑到A是超类型,它不知道B;它只知道thisA,并且可以是任何类型的子类型。所以从静态类型安全的角度来看,此时的类型系统无法知道fooA 的结果也是B 实例。
  • 我不会说 JavaScript“理解”任何关于类型的东西。除了运行时存在的类型安全之外,它没有内置的类型安全性。在编译时,它只是让你为所欲为。
  • FWIW,将fooA 注释为fooA (): this 解决了这个问题。
  • 作为一种解决方法,您可以将成员 fooA: () => B; 添加到 B 类。更好的解决方案是为此 npm 包提供类型

标签: javascript inheritance flowtype


【解决方案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 类:另一个简单的方法是输入B 类的fooA 函数以返回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!

【讨论】:

    【解决方案2】:

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

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

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

    Javascript(无流)不关心这种语义。当您在 fooA() 返回的对象上调用“fooB”时,它只是在对象中查找一个名为“fooB”的方法,该方法恰好存在,因此它可以工作,尽管它更容易因重构而中断。 Flow 很有帮助,因为它会迫使您了解正在抛出的类型,并在将来为您提供这些问题的编译时检查。

    【讨论】:

    • 我只是在他们的网站上测试他们的类型转换,他们的转换似乎仍然要求它是可证明的安全的。我尝试了同一行代码,但它仍然给出错误。 flow.org/try/…
    • 它似乎与Object 一起编译:(new B().fooA(): Object).fooB(); flow.org/try/…
    • @CrazyTrain:将值转换为Object 可以让您访问任何属性(即使它不存在)。这就像一个通配符。
    • @FelixKling:是的,我认为它基本上恢复为“不安全”的 JS。您应该发布您找到的解决方案,因为它似乎描述了 OP 在类型系统中想要什么。
    • @CrazyTrain:OP 想要一个不需要更改 A 的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2016-12-31
    • 2017-08-13
    • 1970-01-01
    • 2018-02-26
    • 2016-07-11
    • 1970-01-01
    • 2010-11-22
    • 1970-01-01
    相关资源
    最近更新 更多