类
Flow 中的 JavaScript 类 既作为值又作为类型运行。你可以使用类的名称作为其实例的类型:
¥JavaScript classes in Flow operate both as a value and a type. You can use the name of the class as the type of its instances:
1class MyClass {2 // ...3}4
5const myInstance: MyClass = new MyClass(); // Works!
这是因为 Flow 中的类是 名义上类型化。
¥This is because classes in Flow are nominally typed.
这意味着具有相同形状的两个类不兼容:
¥This means two classes with identical shapes are not compatible:
1class A {2 x: number;3}4class B {5 x: number;6}7const foo: B = new A(); // Error!8const bar: A = new B(); // Error!
你也不能使用 对象类型 来描述类的实例:
¥You also cannot use an object type to describe an instance of a class:
1class MyClass {2 x: number;3}4const foo: {x: number, ...} = new MyClass(); // Error!
你可以使用 接口 来完成此操作:
¥You can use interfaces to accomplish this instead:
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!
类语法
¥Class Syntax
Flow 中的类就像普通的 JavaScript 类一样,但添加了类型。
¥Classes in Flow are just like normal JavaScript classes, but with added types.
类方法
¥Class Methods
就像 函数 中一样,类方法可以具有参数(输入)和返回(输出)的注释:
¥Just like in functions, class methods can have annotations for both parameters (input) and returns (output):
1class MyClass {2 method(value: string): number {3 return 0;4 }5}
同样,就像常规函数一样,类方法也可能具有 this
注释。但是,如果未提供,Flow 将推断类实例类型(或静态方法的类类型)而不是 mixed
。当提供显式 this
参数时,它必须是类实例类型(或静态方法的类类型)的 超型。
¥Also just like regular functions, class methods may have this
annotations as well.
However, if one is not provided, Flow will infer the class instance type (or the class type for static methods)
instead of mixed
. When an explicit this
parameter is provided, it must be a supertype of
the class instance type (or class type for static methods).
1class MyClass {2 method(this: interface {x: string}) { /* ... */ } // Error!3}
然而,与类属性不同的是,类方法不能从定义它们的类中解除绑定或重新绑定。因此,以下所有内容都是 Flow 中的错误:
¥Unlike class properties, however, class methods cannot be unbound or rebound from the class on which you defined them. So all of the following are errors in Flow:
1class MyClass { method() {} }2const a = new MyClass();3a.method; // Error!4const {method} = a; // Error!5a.method.bind({}); // Error!
方法被认为是 只读:
¥Methods are considered read-only:
1class MyClass {2 method() {}3}4
5const a = new MyClass();6a.method = function() {}; // Error!
Flow 支持 私有方法,这是 ES2022 的一项功能。私有方法以哈希符号 #
开头:
¥Flow supports private methods,
a feature of ES2022. Private methods start with a hash symbol #
:
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!
在大多数情况下,Flow 需要方法上的返回类型注释。这是因为在方法内部引用 this
是很常见的,而 this
被键入为类的实例 - 但要知道类的类型,我们需要知道其方法的返回类型!
¥Flow requires return type annotations on methods in most cases.
This is because it is common to reference this
inside of a method, and this
is typed as the instance of the class -
but to know the type of the class we need to know the return type of its methods!
1class MyClass {2 foo() { // Error!3 return this.bar();4 }5 bar() { // Error!6 return 1;7 }8}
1class MyClassFixed {2 foo(): number { // Works!3 return this.bar();4 }5 bar(): number { // Works!6 return 1;7 }8}
类字段(属性)
¥Class Fields (Properties)
每当你想在 Flow 中使用类字段时,你必须首先给它一个注释:
¥Whenever you want to use a class field in Flow you must first give it an annotation:
1class MyClass {2 method() {3 this.prop = 42; // Error!4 }5}
字段在类的主体中进行注释,字段名称后跟冒号 :
和类型:
¥Fields are annotated within the body of the class with the field name followed
by a colon :
and the type:
1class MyClass {2 prop: number;3 method() {4 this.prop = 42;5 }6}
在类定义之外添加的字段需要在类的主体内进行注释:
¥Fields added outside of the class definition need to be annotated within the body of the class:
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 还支持使用 类属性语法:
¥Flow also supports using the class properties syntax:
1class MyClass {2 prop = 42;3}
使用此语法时,不需要为其提供类型注释。但如果你需要的话,你仍然可以:
¥When using this syntax, you are not required to give it a type annotation. But you still can if you need to:
1class MyClass {2 prop: number = 42;3}
你可以使用 方差 注释将类字段标记为只读(或只写)。这些只能在构造函数中写入:
¥You can mark a class field as read-only (or write-only) using variance annotations. These can only be written to in the constructor:
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!
Flow 支持 私有字段,这是 ES2022 的一项功能。私有字段以哈希符号 #
开头:
¥Flow supports private fields,
a feature of ES2022. Private fields start with a hash symbol #
:
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!
扩展类和实现接口
¥Extending classes and implementing interfaces
你可以选择 extend
另一类:
¥You can optionally extend
one other class:
1class Base {2 x: number;3}4
5class MyClass extends Base {6 y: string;7}
并且还实现多个 接口:
¥And also implement multiple interfaces:
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
为它的子类型,但这样做会强制你的类满足要求:
¥You don't need to implement
an interface to be a subtype of it, but doing so enforces that your class meets the requirements:
1interface WithXNum {2 x: number;3}4
5class MyClass implements WithXNum { // Error!6}
类构造函数
¥Class Constructors
你可以在类构造函数中初始化类属性:
¥You can initialize your class properties in class constructors:
1class MyClass {2 foo: number;3
4 constructor() {5 this.foo = 1;6 }7}
你必须先在派生类中调用 super(...)
,然后才能访问 this
和 super
:
¥You must first call super(...)
in a derived class before you can access this
and super
:
1class Base {2 bar: number;3}4
5class MyClass extends Base {6 foo: number;7
8 constructor() {9 this.foo; // Error10 this.bar; // Error11 super.bar; // Error12 super();13 this.foo; // OK14 this.bar; // OK15 super.bar; // OK16 }17}
但是,Flow 不会强制所有类属性都在构造函数中初始化:
¥However, Flow will not enforce that all class properties are initialized in constructors:
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}
泛型类
¥Class Generics
类也可以有自己的 泛型:
¥Classes can also have their own generics:
1class MyClass<A, B, C> {2 property: A;3 method(val: B): C {4 throw new Error();5 }6}
类泛型是 参数化的。当你使用类作为类型时,你需要为其每个泛型传递参数:
¥Class generics are parameterized. When you use a class as a type you need to pass parameters for each of its generics:
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');
注释中的类
¥Classes in annotations
当你在注释中使用类的名称时,它意味着你的类的一个实例:
¥When you use the name of your class in an annotation, it means an instance of your class:
class MyClass {}
const b: MyClass = new MyClass(); // Works!
const a: MyClass = MyClass; // Error!
有关 Class<T>
的详细信息,请参阅 此处,它允许你在注释中引用类的类型。
¥See here for details on Class<T>
, which allows you
to refer to the type of the class in an annotation.