接口类型(Interface Types)
Flow中的类只是被名义上注解。这意味着,当你有两个单独的类时,即使它们具有相同的确切的属性和方法,也不能相互替代。
// @flow
class Foo {
serialize() { return '[Foo]'; }
}
class Bar {
serialize() { return '[Bar]'; }
}
// $ExpectError
const foo: Foo = new Bar(); // Error!
相反,你可以使用接口来声明你所期望的类的结构。
// @flow
interface Serializable {
serialize(): string;
}
class Foo {
serialize() { return '[Foo]'; }
}
class Bar {
serialize() { return '[Bar]'; }
}
const foo: Serializable = new Foo(); // Works!
const bar: Serializable = new Bar(); // Works!
你也可以使用implements
来告诉Flow你想让这个类匹配一个接口。 这可以防止编辑类时发生不兼容的更改。
// @flow
interface Serializable {
serialize(): string;
}
class Foo implements Serializable {
serialize() { return '[Foo]'; } // Works!
}
class Bar implements Serializable {
// $ExpectError
serialize() { return 42; } // Error!
}
你也可以实现多个接口。
class Foo implements Bar, Baz {
// ...
}
接口语法(Interface Syntax)
interface MyInterface {
// ...
}
接口方法(Interface Methods)
interface MyInterface {
method(value: string): number;
}
接口属性
interface MyInterface {
property: string;
// property?: string;
// [key: string]: number;
}
接口泛型(Interface Generics)
interface MyInterface<A, B, C> {
property: A;
method(val: B): C;
}
接口泛型被参数化。当你使用一个接口时,你需要为每个泛型传递参数。
// @flow
interface MyInterface<A, B, C> {
foo: A;
bar: B;
baz: C;
}
var val: MyInterface<number, boolean, string> = {
foo: 1,
bar: true,
baz: 'three',
};
接口属性差异(只读和只写)
接口属性默认是不变的。 但是你可以添加修饰符来使它们协变(只读)或逆变(只写)。
interface MyInterface {
+covariant: number; // read-only
-contravariant: number; // write-only
}
接口上的协变(只读)属性
你可以通过在属性名称前添加加号+
来创建属性协变。
interface MyInterface {
+readOnly: number | string;
}
// @flow
// $ExpectError
interface Invariant { property: number | string }
interface Covariant { +readOnly: number | string }
var value1: Invariant = { property: 42 }; // Error!
var value2: Covariant = { readOnly: 42 }; // Works!
function method1(value: Invariant) {
value.property; // Works!
value.property = 3.14; // Works!
}
function method2(value: Covariant) {
value.readOnly; // Works!
// $ExpectError
value.readOnly = 3.14; // Error!
}
接口上的逆变(只写)属性
你可以通过在属性名称前添加加号-
来创建属性协变。
interface InterfaceName {
-writeOnly: number;
}
// @flow
interface Invariant { property: number }
interface Contravariant { -writeOnly: number }
var numberOrString = Math.random() > 0.5 ? 42 : 'forty-two';
// $ExpectError
var value1: Invariant = { property: numberOrString }; // Error!
var value2: Contravariant = { writeOnly: numberOrString }; // Works!
function method1(value: Invariant) {
value.property; // Works!
value.property = 3.14; // Works!
}
function method2(value: Contravariant) {
// $ExpectError
value.writeOnly; // Error!
value.writeOnly = 3.14; // Works!
}
泛型(Generic Types)
使用泛型添加抽象(多态)类型。
泛型(有时被称为多态类型)是一种抽象类型的方法。
下面的identity
函数返回任何传入值。但你会发现很难为其定义特定类型,因为有无数种可能。
function identity(value) {
return value;
}
function identity(value: string): string {
return value;
}
相反,我们可以在我们的函数中创建一个泛型(或多态类型),并用它来代替其他类型。
function identity<T>(value: T): T {
return value;
}
泛型可以在函数,函数类型,类,类型别名以及接口中使用。
泛型语法
泛型函数
function method<T>(param: T): T {
// ...
}
function<T>(param: T): T {
// ...
}
泛型函数类型
<T>(param: T) => T
function method(func: <T>(param: T) => T) {
// ...
}
泛型类
class Item<T> {
prop: T;
constructor(param: T) {
this.prop = param;
}
method(): T {
return this.prop;
}
}
泛型类型别名
type Item<T> = {
foo: T,
bar: T,
};
泛型接口
interface Item<T> {
foo: T,
bar: T,
}
泛型行为
泛型变量行为
function constant<T>(value: T) {
return function(): T {
return value;
};
}
泛型值跟踪
// @flow
function identity<T>(value: T): T {
return value;
}
let one: 1 = identity(1);
let two: 2 = identity(2);
// $ExpectError
let three: 3 = identity(42);
为泛型添加类型
类似mixed
类型,泛型有一个“未知”类型。 你不能像使用特定类型那样使用泛型。
// @flow
function logFoo<T>(obj: T): T {
// $ExpectError
console.log(obj.foo); // Error!
return obj;
}
相反,你可以添加一个类型到你的泛型,就像你用一个函数参数。
// @flow
function logFoo<T: { foo: string }>(obj: T): T {
console.log(obj.foo); // Works!
return obj;
}
logFoo({ foo: 'foo', bar: 'bar' }); // Works!
// $ExpectError
logFoo({ bar: 'bar' }); // Error!
function identity<T: number>(value: T): T {
return value;
}
let one: 1 = identity(1);
let two: 2 = identity(2);
// $ExpectError
let three: "three" = identity("three");
泛型类型绑定
在Flow中,当你将一种类型传递给另一种时,多数情况下你会失去原有的类型。
// @flow
function identity(val: string): string {
return val;
}
let foo: 'foo' = 'foo'; // Works!
// $ExpectError
let bar: 'bar' = identity('bar'); // Error!
泛型允许你在添加约束的同时保持更具体的类型。 通过这种方式达到泛型的类型绑定。
// @flow
function identity<T: string>(val: T): T {
return val;
}
let foo: 'foo' = 'foo'; // Works!
let bar: 'bar' = identity('bar'); // Works!
当一个值使用了泛型类型绑定时,你不能使用更具体的类型重新使用它。
// @flow
function identity<T: string>(val: T): T {
let str: string = val; // Works!
// $ExpectError
let bar: 'bar' = val; // Error!
return val;
}
identity('bar');
参数化泛型
泛型有时候允许你将类型参数传递给一个函数。这些被称为参数化的泛型(或参数多态性)。
当你去使用参数化泛型时,你需要提供一个类型参数。
type Item<T> = {
prop: T,
}
let item: Item<string> = {
prop: "value"
};
类,类型别名和接口都需要传递类型参数。 函数和函数类型没有参数化泛型。
类
// @flow
class Item<T> {
prop: T;
constructor(param: T) {
this.prop = param;
}
}
let item1: Item<number> = new Item(42); // Works!
// $ExpectError
let item2: Item = new Item(42); // Error!
类型别名
// @flow
type Item<T> = {
prop: T,
};
let item1: Item<number> = { prop: 42 }; // Works!
// $ExpectError
let item2: Item = { prop: 42 }; // Error!
接口
// @flow
interface HasProp<T> {
prop: T,
}
class Item {
prop: string;
}
(Item.prototype: HasProp<string>); // Works!
// $ExpectError
(Item.prototype: HasProp); // Error!
参数化泛型默认参数
type Item<T: number = 1> = {
prop: T,
};
let foo: Item<> = { prop: 1 };
let bar: Item<2> = { prop: 2 };