接口
类 在 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]