跳至主要内容

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}

在派生类中访问 thissuper 之前,您必须先调用 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> 的详细信息,请参见 此处,它允许您在注解中引用类的类型。