什么时候在流中通过类型别名使用接口?

When do you use an interface over a type alias in flow?

interfacetype 声明似乎做同样的事情。你什么时候使用一个而不是另一个?

type Fooable = {
  foo(): string
}

interface Fooable {
 foo(): string
}

这是一个很好的问题。理想情况下,接口和对象类型之间没有区别。在实施时,它们之间存在一些(通常是细微的)差异。

最大的区别是 Flow 认为在接口上声明的方法是 "read-only." 这允许子类型是协变的 w.r.t。方法,这是一种非常常见的继承层次结构模式。

我希望及时看到 Flow 统一这些概念,但在此之前,这是我在接口和对象类型之间进行选择的经验法则:

  • 使用对象类型来描述在您的应用程序中传递的主要数据包,例如,props/state 用于 React 组件,Flux/Redux 操作,JSON 之类的东西。
  • 使用接口来描述类似服务的接口。通常这些主要是方法,例如 Rx.Observable/Observer、Flux/Redux 存储、抽象接口。如果一个 class 实例很可能是你的类型的居民,你可能需要一个接口。

希望对您有所帮助!

Flow 中的接口可用于确保 class 实现某些方法和属性。例如:

interface IFly {
   fly(): string
}

// Good!
class Duck implements IFly {
    fly() {
        return "I believe I can fly"
    }
}

// Bad! Cannot implement `IFly` with `Duck` because number is incompatible with string in the return value of property `fly`.
class Duck implements IFly {
    fly() {
        return 42
    }
}

// Bad! Cannot implement `IFly` with `Duck` because property `fly` is missing in `Duck` but exists in `IFly`.
class Duck implements IFly {
    quack() {
        return "quack quack"
    }
}

但是,如果我们定义等效的 IFly 类型,我们的 Duck class 将无法实现它:

type IFly = {
  fly(): string
}

// Cannot implement `Fly` because it is not an interface.
class Duck implements Fly {
    fly() {
       return "I believe I can fly"
    }
}

此外,类型和接口之间还有更多细微差别。

默认情况下,接口属性是不变的。例如:

interface Foo {
    property: string | number
}

let foo: Foo = { property: 42 } // Cannot assign object literal to `foo` because number is incompatible with string in property `property`.

要使接口 属性 协变 它们需要设为只读:

interface Foo {
    +property: string | number
}

let foo: Foo = { property: 42 } // Good!

另一方面,对于类型,Flow 不会抱怨:

type Foo = {
    property: string | number
}

// Good!
let foo: Foo = { property: 42 }

参考文献:

  1. Flow interfaces
  2. Type variance