Skip to main content

接口

Flow 中的 名义上类型化。这意味着当你有两个单独的类时,即使它们具有完全相同的属性和方法,你也不能使用其中一个来代替另一个:

¥Classes in Flow are nominally typed. This means that when you have two separate classes you cannot use one in place of the other even when they have the same exact properties and methods:

1class Foo {2  serialize(): string { return '[Foo]'; }3}4
5class Bar {6  serialize(): string { return '[Bar]'; }7}8
9const foo: Foo = new Bar(); // Error!

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

¥Instead, you can use interface in order to declare the structure of the class that you are expecting.

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!

你还可以声明一个匿名接口:

¥You can also declare an anonymous interface:

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 你希望该类与接口匹配。这可以防止你在编辑类时进行不兼容的更改。

¥You can also use implements to tell Flow that you want the class to match an interface. This prevents you from making incompatible changes when editing the class.

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}

你还可以将 implements 与多个接口一起使用。

¥You can also use implements with multiple interfaces.

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

接口可以描述实例和对象,与只能描述对象的对象类型不同:

¥Interfaces can describe both instances and objects, unlike object types which can only describe objects:

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!

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

¥Unlike objects, interfaces cannot be exact, as they can always have other, unknown properties.

接口语法

¥Interface Syntax

接口是使用关键字 interface 后跟其名称和包含类型定义主体的块创建的。

¥Interfaces are created using the keyword interface followed by its name and a block which contains the body of the type definition.

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

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

¥The syntax of the block matches the syntax of object types.

接口方法

¥Interface Methods

你可以按照与类方法相同的语法向接口添加方法。你提供的任何 this 参数 也受到与类方法相同的限制。

¥You can add methods to interfaces following the same syntax as class methods. Any this parameters you provide are also subject to the same restrictions as class methods.

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

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

¥Also like class methods, interface methods must also remain bound to the interface on which they were defined.

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

¥You can define overloaded methods by declaring the same method name multiple times with different type signatures:

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}

接口属性

¥Interface Properties

你可以按照与类属性相同的语法向接口添加属性:

¥You can add properties to interfaces following the same syntax as class properties:

1interface MyInterface {2  property: string;3}

接口属性也可以是可选的:

¥Interface properties can be optional as well:

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

接口作为映射

¥Interfaces as maps

你可以像创建对象一样创建 索引器属性

¥You can create indexer properties the same way as with objects:

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

接口泛型

¥Interface Generics

接口也可以有自己的 泛型

¥Interfaces can also have their own generics:

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

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

¥Interface generics are parameterized. When you use an interface you need to pass parameters for each of its generics:

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};

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

¥Interface property variance (read-only and write-only)

接口属性默认为 不变的。但你可以添加修饰符以使它们成为协变(只读)或逆变(只写)。

¥Interface properties are invariant by default. But you can add modifiers to make them covariant (read-only) or contravariant (write-only).

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

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

¥Covariant (read-only) properties on interfaces

你可以通过在属性名称前面添加加号 + 来使属性协变:

¥You can make a property covariant by adding a plus symbol + in front of the property name:

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

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

¥This allows you to pass a more specific type in place of that property:

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

由于协变的工作原理,协变属性在使用时也会变为只读。这比普通属性更有用。

¥Because of how covariance works, covariant properties also become read-only when used. Which can be useful over normal properties.

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}

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

¥Contravariant (write-only) properties on interfaces

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

¥You can make a property contravariant by adding a minus symbol - in front of the property name.

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

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

¥This allows you to pass a less specific type in place of that property.

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!

由于逆变的工作原理,逆变属性在使用时也会变为只写。这比普通属性更有用。

¥Because of how contravariance works, contravariant properties also become write-only when used. Which can be useful over normal properties.

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}