flow中文文档(七)


接口类型

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,后跟其名称和包含类型定义主体的块创建的。

interface MyInterface {
  // ...
}

块的语法与对象类型的语法匹配,并具有所有相同的功能


接口方法

您可以按照与对象方法相同的语法向接口添加方法。

interface MyInterface {
  method(value: string): number;
}

接口属性

您可以按照与对象属性相同的语法向接口添加属性。

interface MyInterface {
  property: string;
}

接口属性也是可选的。

interface MyInterface {
  property?: string;
}

接口Maps

您可以使用与对象相同的方式创建“索引器属性”。

interface MyInterface {
  [key: string]: number;
}

接口泛型

接口也可以有自己的泛型。

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!

由于协方差的工作原理,协变属性在使用时也变为只读。这对普通属性有用。

// @flow
interface Invariant {  property: number | string }
interface Covariant { +readOnly: number | string }

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!

由于逆变的工作原理,逆变属性在使用时也会变为只写。这对普通属性有用。

interface Invariant     {   property: number }
interface Contravariant { -writeOnly: number }

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!
}

泛型

泛型(有时称为多态类型)是一种抽象类型的方法。 想象一下编写以下身份函数,它返回传递的任何值。

function identity(value) {
  return value;
}

我们在尝试为此函数编写特定类型时会遇到很多麻烦,因为它可能是任何东西。

function identity(value: string): string {
  return value;
}

相反,我们可以在函数中创建泛型(或多态类型),并使用它代替其他类型。

function identity<T>(value: T): T {
  return value;
}

泛型可以在函数,函数类型,类,类型别名和接口中使用。

警告:Flow不会推断泛型类型。如果您希望某些东西具有泛型类型,请对其进行注释。否则,Flow可能会推断出比您预期的多态性更低的类型。

在下面的示例中,我们忘记使用泛型类型正确地注释 identity ,因此当我们尝试将其分配给func时会遇到麻烦。另一方面,genericIdentity已正确输入,我们可以按预期使用它。

// @flow

type IdentityWrapper = {
  func<T>(T): T
}

function identity(value) {
  return value;
}

function genericIdentity<T>(value: T): T {
  return value;
}

// $ExpectError
const bad: IdentityWrapper = { func: identity }; // Error!
const good: IdentityWrapper = { func: genericIdentity }; // Works!

具有泛型的函数

函数可以通过在函数参数列表之前添加类型参数列表<T>来创建泛型。 您可以在函数(参数或返回类型)中添加任何其他类型的相同位置使用泛型。

function method<T>(param: T): T {
  // ...
}

function<T>(param: T): T {
  // ...
}

具有泛型的函数类型

函数类型可以通过在函数类型参数列表之前添加类型参数list <T>,以与普通函数相同的方式创建泛型。 您可以在函数类型(参数或返回类型)中添加任何其他类型的相同位置使用泛型。

<T>(param: T) => T

然后将其用作自己的类型。

function method(func: <T>(param: T) => T) {
  // ...
}

具有泛型的类

类可以通过将类型参数列表放在类的主体之前来创建泛型。

class Item<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;
  };
}

根据需要创建尽可能多的泛型

您可以在类型参数列表中根据需要使用这些泛型,并根据需要命名它们:

function identity<One, Two, Three>(one: One, two: Two, three: Three) {
  // ...
}

泛型跟踪

当对值使用泛型类型时,Flow将跟踪该值并确保您不会将其替换为其他值。

// @flow
function identity<T>(value: T): T {
  // $ExpectError
  return "foo"; // Error!
}

function identity<T>(value: T): T {
  // $ExpectError
  value = "foo"; // Error!
  // $ExpectError
  return value;  // Error!
}

Flow跟踪您通过泛型的值的特定类型,以便稍后使用。

// @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>(obj: T): T {
  if (obj && obj.foo) {
    console.log(obj.foo); // Works.
  }
  return obj;
}

logFoo({ foo: 'foo', bar: 'bar' });  // Works.
logFoo({ bar: 'bar' }); // Works. :(

相反,您可以像使用函数参数一样为泛型添加类型。

// @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!

这样,您可以保留泛型的行为,同时仅允许使用某些类型。

// @flow
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
function identity<T>(val: T): T {
  return val;
}

let foo: 'foo' = 'foo';           // Works!
let bar: 'bar' = identity('bar'); // Works!

在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"
};

您可以将此视为将函数传递给函数,只有返回值是您可以使用的类型。

类(当用作类型时),类型别名和接口都要求您传递类型参数。函数和函数类型没有参数化泛型。

classes

// @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!

Type Aliases 类型别名

// @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 };

使用类型时,必须始终包括括号<>(就像函数调用的括号一样)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,600评论 18 139
  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,732评论 2 9
  • http://liuxing.info/2017/06/30/Spring%20AMQP%E4%B8%AD%E6%...
    sherlock_6981阅读 15,873评论 2 11
  • 从小听过了那么多大道理和经验之谈,却依旧选了坑坑洼洼的路,走得曲曲折折,把自己摔得鼻青脸肿,许是再好的道理也要自己...
    轻尘印阅读 346评论 0 0
  • 文|九月流云 参赛编号: 079 周六一早,我坐上高铁,趁周末,去替换多日来连续给大哥陪床的大嫂和侄子...
    九月流云阅读 653评论 19 33