跳至主要内容

接口

在 Flow 中是 名义类型。这意味着当您有两个独立的类时,即使它们具有完全相同的属性和方法,您也不能将其中一个用作另一个的替代。

1class Foo {2  serialize(): string { return '[Foo]'; }3}4
5class Bar {6  serialize(): string { return '[Bar]'; }7}8
9const foo: Foo = new Bar(); // Error!
9:18-9:26: Cannot assign `new Bar()` to `foo` because `Bar` [1] is incompatible with `Foo` [2]. [incompatible-type]

相反,您可以使用 interface 来声明您期望的类的结构。

1interface Serializable {2  serialize(): string;3}4
5class Foo {6  serialize(): string { return '[Foo]'; }7}8
9class Bar {10  serialize(): string { return '[Bar]'; }11}12
13const foo: Serializable = new Foo(); // Works!14const bar: Serializable = new Bar(); // Works!

您也可以声明一个匿名接口

1class Foo {2  a: number;3}4
5function getNumber(o: interface {a: number}): number {6  return o.a;7}8
9getNumber(new Foo()); // Works!

您还可以使用 implements 告诉 Flow 您希望该类匹配一个接口。这可以防止您在编辑类时进行不兼容的更改。

1interface Serializable {2  serialize(): string;3}4
5class Foo implements Serializable {6  serialize(): string { return '[Foo]'; } // Works!7}8
9class Bar implements Serializable {10  serialize(): number { return 42; } // Error!
11}
10:16-10:21: Cannot implement `Serializable` [1] with `Bar` because number [2] is incompatible with string [3] in the return value of property `serialize`. [incompatible-type]

您也可以将 implements 与多个接口一起使用。

class Foo implements Bar, Baz {
// ...
}

接口可以描述实例和对象,而对象类型只能描述对象。

1class Foo {2  a: number;3}4const foo = new Foo();5const o: {a: number} = {a: 1};6
7interface MyInterface {8  a: number;9}10
11function acceptsMyInterface(x: MyInterface) { /* ... */ }12acceptsMyInterface(o); // Works!13acceptsMyInterface(foo); // Works!14
15function acceptsObj(x: {a: number, ...}) { /* ... */ }16acceptsObj(o); // Works!17acceptsObj(foo); // Error!
17:12-17:14: Cannot call `acceptsObj` with `foo` bound to `x` because `Foo` [1] is not a subtype of object type [2]. Class instances are not subtypes of object types; consider rewriting object type [2] as an interface. [class-object-subtyping]

与对象不同,接口不能是 精确的,因为它们始终可以具有其他未知属性。

接口语法

接口使用关键字 interface 以及其名称和包含类型定义主体块来创建。

1interface MyInterface {2  // ...3}

块的语法与对象类型的语法匹配。

接口方法

您可以使用与类方法相同的语法将方法添加到接口。您提供的任何 this 参数 也受与类方法相同的限制。

1interface MyInterface {2  method(value: string): number;3}

类方法 一样,接口方法也必须保持绑定到定义它们的接口。

您可以通过使用不同的类型签名多次声明相同的方法名称来定义 重载方法

1interface MyInterface {2  method(value: string): string;3  method(value: boolean): boolean;4}5
6function func(a: MyInterface) {7  const x: string = a.method('hi'); // Works!8  const y: boolean = a.method(true); // Works!9
10  const z: boolean = a.method('hi'); // Error!
11}
10:22-10:35: Cannot assign `a.method(...)` to `z` because string [1] is incompatible with boolean [2]. [incompatible-type]

接口属性

您可以使用与类属性相同的语法将属性添加到接口

1interface MyInterface {2  property: string;3}

接口属性也可以是可选的

1interface MyInterface {2  property?: string;3}

接口作为映射

您可以像使用对象一样创建 索引器属性

1interface MyInterface {2  [key: string]: number;3}

接口泛型

接口也可以有自己的 泛型

1interface MyInterface<A, B, C> {2  property: A;3  method(val: B): C;4}

接口泛型是 参数化的。当您使用接口时,您需要为其每个泛型传递参数

1interface MyInterface<A, B, C> {2  foo: A;3  bar: B;4  baz: C;5}6
7const val: MyInterface<number, boolean, string> = {8  foo: 1,9  bar: true,10  baz: 'three',11};

接口属性变异(只读和只写)

接口属性默认情况下是 不变的。但是,您可以添加修饰符以使其成为协变(只读)或逆变(只写)。

1interface MyInterface {2  +covariant: number;     // read-only3  -contravariant: number; // write-only4}

接口上的协变(只读)属性

您可以通过在属性名称前面添加加号 + 来使属性成为协变

1interface MyInterface {2  +readOnly: number | string;3}

这允许您传递更具体的类型来代替该属性

1interface Invariant {2  property: number | string;3}4interface Covariant {5  +readOnly: number | string;6}7
8const x: {property: number} = {property: 42};9const y: {readOnly: number} = {readOnly: 42};10
11const value1: Invariant = x; // Error!
12const value2: Covariant = y; // Works
11:27-11:27: Cannot assign `x` to `value1` because string [1] is incompatible with number [2] in property `property`. This property is invariantly typed. See https://flow.node.org.cn/en/docs/faq/#why-cant-i-pass-a-string-to-a-function-that-takes-a-string-number. [incompatible-type]

由于协变的工作方式,协变属性在使用时也会变成只读的。这在普通属性之上可能很有用。

1interface Invariant {2  property: number | string;3}4interface Covariant {5  +readOnly: number | string;6}7
8function func1(value: Invariant) {9  value.property;        // Works!10  value.property = 3.14; // Works!11}12
13function func2(value: Covariant) {14  value.readOnly;        // Works!15  value.readOnly = 3.14; // Error!
16}
15:9-15:16: Cannot assign `3.14` to `value.readOnly` because property `readOnly` is not writable. [cannot-write]

接口上的逆变(只写)属性

您可以通过在属性名称前面添加减号 - 来使属性成为逆变。

1interface InterfaceName {2  -writeOnly: number;3}

这允许您传递更不具体的类型来代替该属性。

1interface Invariant {2  property: number;3}4interface Contravariant {5  -writeOnly: number;6}7
8const numberOrString = Math.random() > 0.5 ? 42 : 'forty-two';9
10const value1: Invariant     = {property: numberOrString};  // Error!
11const value2: Contravariant = {writeOnly: numberOrString}; // Works!
10:42-10:55: Cannot assign object literal to `value1` because string [1] is incompatible with number [2] in property `property`. [incompatible-type]

由于逆变的工作方式,逆变属性在使用时也会变成只写的。这在普通属性之上可能很有用。

1interface Invariant {2  property: number;3}4interface Contravariant {5  -writeOnly: number;6}7
8function func1(value: Invariant) {9  value.property;        // Works!10  value.property = 3.14; // Works!11}12
13function func2(value: Contravariant) {14  value.writeOnly;        // Error!
15 value.writeOnly = 3.14; // Works!16}
14:9-14:17: Cannot get `value.writeOnly` because property `writeOnly` is not readable. [cannot-read]