类
Flow 中的 JavaScript 类 既是值又是类型。您可以使用类的名称作为其实例的类型
1class MyClass {2 // ...3}4
5const myInstance: MyClass = new MyClass(); // Works!
这是因为 Flow 中的类是 名义类型。
这意味着两个形状相同的类不兼容
1class A {2 x: number;3}4class B {5 x: number;6}7const foo: B = new A(); // Error! 8const bar: A = new B(); // Error!
7:16-7:22: Cannot assign `new A()` to `foo` because `A` [1] is incompatible with `B` [2]. [incompatible-type]8:16-8:22: Cannot assign `new B()` to `bar` because `B` [1] is incompatible with `A` [2]. [incompatible-type]
您也不能使用 对象类型 来描述类的实例
1class MyClass {2 x: number;3}4const foo: {x: number, ...} = new MyClass(); // Error!
4:31-4:43: Cannot assign `new MyClass()` to `foo` because `MyClass` [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]
您可以使用 接口 来代替完成此操作
1class A {2 x: number;3}4class B {5 x: number;6}7
8interface WithXNum {9 x: number;10}11
12const foo: WithXNum = new A(); // Works!13const bar: WithXNum = new B(); // Works!14
15const n: number = foo.x; // Works!
类语法
Flow 中的类就像普通的 JavaScript 类一样,但添加了类型。
类方法
就像在 函数 中一样,类方法可以对参数(输入)和返回值(输出)进行注解
1class MyClass {2 method(value: string): number {3 return 0;4 }5}
同样,类方法也可能具有 this
注解。但是,如果没有提供,Flow 将推断类实例类型(或静态方法的类类型),而不是 mixed
。当提供显式 this
参数时,它必须是类实例类型(或静态方法的类类型)的 超类型。
1class MyClass {2 method(this: interface {x: string}) { /* ... */ } // Error! 3}
2:3-2:8: Cannot define method `method` [1] on `MyClass` because property `x` is missing in `MyClass` [2] but exists in interface type [3]. [prop-missing]
但是,与类属性不同,类方法不能从定义它们的类中解绑或重新绑定。因此,以下所有操作在 Flow 中都是错误的
1class MyClass { method() {} }2const a = new MyClass();3a.method; // Error! 4const {method} = a; // Error! 5a.method.bind({}); // Error!
3:3-3:8: Cannot get `a.method` because property `method` [1] cannot be unbound from the context [2] where it was defined. [method-unbinding]4:8-4:13: property `method` [1] cannot be unbound from the context [2] where it was defined. [method-unbinding]5:3-5:8: Cannot get `a.method` because property `method` [1] cannot be unbound from the context [2] where it was defined. [method-unbinding]
方法被认为是 只读
1class MyClass {2 method() {}3}4
5const a = new MyClass();6a.method = function() {}; // Error!
6:3-6:8: Cannot assign function to `a.method` because property `method` is not writable. [cannot-write]
Flow 支持 私有方法,这是 ES2022 的一项功能。私有方法以哈希符号 #
开头
1class MyClass {2 #internalMethod() {3 return 1;4 }5 publicApi() {6 return this.#internalMethod();7 }8}9
10const a = new MyClass();11a.#internalMethod(); // Error! 12a.publicApi(); // Works!
11:3-11:17: Private fields can only be referenced from within a class.
在大多数情况下,Flow 要求方法具有返回值注解。这是因为在方法内部引用 this
很常见,并且 this
的类型被类型化为类的实例 - 但是要了解类的类型,我们需要知道其方法的返回值类型!
1class MyClass {2 foo() { // Error! 3 return this.bar();4 }5 bar() { // Error! 6 return 1;7 }8}
2:8-2:7: Missing an annotation on return. [missing-local-annot]5:8-5:7: Missing an annotation on return. [missing-local-annot]
1class MyClassFixed {2 foo(): number { // Works!3 return this.bar();4 }5 bar(): number { // Works!6 return 1;7 }8}
类字段(属性)
无论何时您想在 Flow 中使用类字段,您都必须先对其进行注解
1class MyClass {2 method() {3 this.prop = 42; // Error! 4 }5}
3:10-3:13: Cannot assign `42` to `this.prop` because property `prop` is missing in `MyClass` [1]. [prop-missing]
字段在类的主体中进行注解,字段名称后跟冒号 :
和类型
1class MyClass {2 prop: number;3 method() {4 this.prop = 42;5 }6}
在类定义之外添加的字段需要在类的主体中进行注解
1function func(x: number): number {2 return x + 1;3}4
5class MyClass {6 static constant: number;7 static helper: (number) => number;8 prop: number => number;9}10MyClass.helper = func11MyClass.constant = 4212MyClass.prototype.prop = func
Flow 还支持使用 类属性语法
1class MyClass {2 prop = 42;3}
使用此语法时,您不需要对其进行类型注解。但如果您需要,您仍然可以进行注解
1class MyClass {2 prop: number = 42;3}
您可以使用 方差 注解将类字段标记为只读(或只写)。这些只能在构造函数中写入
1class MyClass {2 +prop: number;3
4 constructor() {5 this.prop = 1; // Works!6 }7
8 method() {9 this.prop = 1; // Error! 10 }11}12
13const a = new MyClass();14const n: number = a.prop; // Works!15a.prop = 1; // Error!
9:10-9:13: Cannot assign `1` to `this.prop` because property `prop` is not writable. [cannot-write]15:3-15:6: Cannot assign `1` to `a.prop` because property `prop` is not writable. [cannot-write]
Flow 支持 私有字段,这是 ES2022 的一项功能。私有字段以哈希符号 #
开头
1class MyClass {2 #internalValue: number;3
4 constructor() {5 this.#internalValue = 1;6 }7
8 publicApi() {9 return this.#internalValue;10 }11}12
13const a = new MyClass();14const x: number = a.#internalValue; // Error! 15const y: number = a.publicApi(); // Works!
14:21-14:34: Private fields can only be referenced from within a class.
扩展类和实现接口
您可以选择 extend
另一个类
1class Base {2 x: number;3}4
5class MyClass extends Base {6 y: string;7}
还可以实现多个 接口
1interface WithXNum {2 x: number;3}4interface Readable {5 read(): string;6}7
8class MyClass implements WithXNum, Readable {9 x: number;10 read(): string {11 return String(this.x);12 }13}
您不需要 implement
接口来成为其子类型,但这样做会强制您的类满足要求
1interface WithXNum {2 x: number;3}4
5class MyClass implements WithXNum { // Error! 6}
5:7-5:13: Cannot implement `WithXNum` [1] with `MyClass` because property `x` is missing in `MyClass` [2] but exists in `WithXNum` [1]. [prop-missing]
类构造函数
您可以在类构造函数中初始化类属性
1class MyClass {2 foo: number;3
4 constructor() {5 this.foo = 1;6 }7}
在派生类中访问 this
和 super
之前,您必须先调用 super(...)
1class Base {2 bar: number;3}4
5class MyClass extends Base {6 foo: number;7
8 constructor() {9 this.foo; // Error 10 this.bar; // Error 11 super.bar; // Error 12 super();13 this.foo; // OK14 this.bar; // OK15 super.bar; // OK16 }17}
9:5-9:8: Must call `super` before accessing this [1] in a derived constructor. [reference-before-declaration]10:5-10:8: Must call `super` before accessing this [1] in a derived constructor. [reference-before-declaration]11:5-11:9: Must call `super` before accessing super [1] in a derived constructor. [reference-before-declaration]
但是,Flow 不会强制所有类属性都在构造函数中初始化
1class MyClass {2 foo: number;3 bar: number;4
5 constructor() {6 this.foo = 1;7 }8
9 useBar() {10 (this.bar: number); // No errors.11 }12}
类泛型
类也可以有自己的 泛型
1class MyClass<A, B, C> {2 property: A;3 method(val: B): C {4 throw new Error();5 }6}
类泛型是 参数化的。当您将类用作类型时,您需要为其每个泛型传递参数
1class MyClass<A, B, C> {2 constructor(arg1: A, arg2: B, arg3: C) {3 // ...4 }5}6
7const val: MyClass<number, boolean, string> = new MyClass(1, true, 'three');
注解中的类
当您在注解中使用类的名称时,它表示您类的 实例
class MyClass {}
const b: MyClass = new MyClass(); // Works!
const a: MyClass = MyClass; // Error!
有关 Class<T>
的详细信息,请参见 此处,它允许您在注解中引用类的类型。