Skip to main content

泛型

泛型(有时称为多态类型)是一种抽象类型的方法。

¥Generics (sometimes referred to as polymorphic types) are a way of abstracting a type away.

想象一下编写以下 identity 函数,该函数返回传递的任何值。

¥Imagine writing the following identity function which returns whatever value was passed.

function identity(value) {
return value;
}

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

¥We would have a lot of trouble trying to write specific types for this function since it could be anything.

1function identity(value: string): string {2  return value;3}

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

¥Instead we can create a generic (or polymorphic type) in our function and use it in place of other types.

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

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

¥Generics can be used within functions, function types, classes, type aliases, and interfaces.

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

¥Warning: Flow does not infer generic types. If you want something to have a generic type, annotate it. Otherwise, Flow may infer a type that is less polymorphic than you expect.

泛型的语法

¥Syntax of generics

泛型类型出现在语法中的许多不同位置。

¥There are a number of different places where generic types appear in syntax.

带有泛型的函数

¥Functions with generics

函数可以通过在函数参数列表之前添加类型参数列表 <T> 来创建泛型。

¥Functions can create generics by adding the type parameter list <T> before the function parameter list.

你可以在函数中添加任何其他类型(参数或返回类型)的相同位置使用泛型。

¥You can use generics in the same places you'd add any other type in a function (parameter or return types).

1function method<T>(param: T): T {2  return param;3}4
5const f = function<T>(param: T): T {6  return param;7}

带有泛型的函数类型

¥Function types with generics

函数类型可以像普通函数一样创建泛型,只需在函数类型参数列表之前添加类型参数列表 <T> 即可。

¥Function types can create generics in the same way as normal functions, by adding the type parameter list <T> before the function type parameter list.

你可以在函数类型(参数或返回类型)中添加任何其他类型的相同位置使用泛型。

¥You can use generics in the same places you'd add any other type in a function type (parameter or return types).

<T>(param: T) => T

然后它被用作它自己的类型。

¥Which then gets used as its own type.

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

具有泛型的类

¥Classes with generics

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

¥Classes can create generics by placing the type parameter list before the body of the class.

1class Item<T> {2  // ...3}

你可以在类中添加任何其他类型(属性类型和方法参数/返回类型)的相同位置使用泛型。

¥You can use generics in the same places you'd add any other type in a class (property types and method parameter/return types).

1class Item<T> {2  prop: T;3
4  constructor(param: T) {5    this.prop = param;6  }7
8  method(): T {9    return this.prop;10  }11}

具有泛型的类型别名

¥Type aliases with generics

1type Item<T> = {2  foo: T,3  bar: T,4};

使用泛型的接口

¥Interfaces with generics

1interface Item<T> {2  foo: T,3  bar: T,4}

向 Callables 提供类型参数

¥Supplying Type Arguments to Callables

你可以直接在调用中为其泛型提供可调用实体类型参数:

¥You can give callable entities type arguments for their generics directly in the call:

1function doSomething<T>(param: T): T {2  // ...3  return param;4}5
6doSomething<number>(3);

你还可以直接在 new 表达式中给出泛型类类型参数:

¥You can also give generic classes type arguments directly in the new expression:

1class GenericClass<T> {}2const c = new GenericClass<number>();

如果你只想指定某些类型参数,你可以使用 _ 让 flow 为你推断类型:

¥If you only want to specify some of the type arguments, you can use _ to let flow infer a type for you:

1class GenericClass<T, U=string, V=number>{}2const c = new GenericClass<boolean, _, string>();

警告:出于性能目的,我们始终建议你尽可能使用具体参数进行注释。_ 并非不安全,但它比显式指定类型参数慢。

¥Warning: For performance purposes, we always recommend you annotate with concrete arguments when you can. _ is not unsafe, but it is slower than explicitly specifying the type arguments.

泛型的行为

¥Behavior of generics

泛型的作用类似于变量

¥Generics act like variables

泛型类型的工作方式很像变量或函数参数,只是它们用于类型。只要它们在范围内,你就可以使用它们。

¥Generic types work a lot like variables or function parameters except that they are used for types. You can use them whenever they are in scope.

1function constant<T>(value: T): () => T {2  return function(): T {3    return value;4  };5}

根据需要创建尽可能多的仿制药

¥Create as many generics as you need

你可以根据需要在类型参数列表中包含任意数量的泛型,并为它们命名为你想要的名称:

¥You can have as many of these generics as you need in the type parameter list, naming them whatever you want:

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

泛型跟踪左右的值

¥Generics track values around

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

¥When using a generic type for a value, Flow will track the value and make sure that you aren't replacing it with something else.

1function identity<T>(value: T): T {2  return "foo"; // Error!3}4
5function identity<T>(value: T): T {6  value = "foo"; // Error!7  return value;  // Error!8}

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

¥Flow tracks the specific type of the value you pass through a generic, letting you use it later.

1function identity<T>(value: T): T {2  return value;3}4
5let one: 1 = identity(1);6let two: 2 = identity(2);7let three: 3 = identity(42); // Error

向泛型添加类型

¥Adding types to generics

mixed 类似,泛型也有 "未知" 类型。不允许你像使用特定类型一样使用泛型。

¥Similar to mixed, generics have an "unknown" type. You're not allowed to use a generic as if it were a specific type.

1function logFoo<T>(obj: T): T {2  console.log(obj.foo); // Error!3  return obj;4}

你可以优化类型,但泛型仍然允许传入任何类型。

¥You could refine the type, but the generic will still allow any type to be passed in.

1function logFoo<T>(obj: T): T {2  if (obj && obj.foo) {3    console.log(obj.foo); // Works.4  }5  return obj;6}7
8logFoo({ foo: 'foo', bar: 'bar' });  // Works.9logFoo({ bar: 'bar' }); // Works. :(

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

¥Instead, you could add a type to your generic like you would with a function parameter.

1function logFoo<T: {foo: string, ...}>(obj: T): T {2  console.log(obj.foo); // Works!3  return obj;4}5
6logFoo({ foo: 'foo', bar: 'bar' });  // Works!7logFoo({ bar: 'bar' }); // Error!

通过这种方式,你可以保留泛型的行为,同时只允许使用某些类型。

¥This way you can keep the behavior of generics while only allowing certain types to be used.

1function identity<T: number>(value: T): T {2  return value;3}4
5let one: 1 = identity(1);6let two: 2 = identity(2);7let three: "three" = identity("three"); // Error!

泛型类型充当边界

¥Generic types act as bounds

1function identity<T>(val: T): T {2  return val;3}4
5let foo: 'foo' = 'foo';           // Works!6let bar: 'bar' = identity('bar'); // Works!

在 Flow 中,大多数时候,当你将一种类型传递到另一种类型时,你会丢失原始类型。因此,当你将特定类型传递到不太特定的 Flow "忘记了" 时,它曾经是更特定的东西。

¥In Flow, most of the time when you pass one type into another you lose the original type. So that when you pass a specific type into a less specific one Flow "forgets" it was once something more specific.

1function identity(val: string): string {2  return val;3}4
5let foo: 'foo' = 'foo';           // Works!6let bar: 'bar' = identity('bar'); // Error!

泛型允许你在添加约束时保留更具体的类型。这样,泛型上的类型就充当 "界限"。

¥Generics allow you to hold onto the more specific type while adding a constraint. In this way types on generics act as "bounds".

1function identity<T: string>(val: T): T {2  return val;3}4
5let foo: 'foo' = 'foo';           // Works!6let bar: 'bar' = identity('bar'); // Works!

请注意,当你有一个具有绑定泛型类型的值时,你不能将它当作更具体的类型来使用。

¥Note that when you have a value with a bound generic type, you can't use it as if it were a more specific type.

1function identity<T: string>(val: T): T {2  let str: string = val; // Works!3  let bar: 'bar'  = val; // Error!4  return val;5}6
7identity('bar');

参数化泛型

¥Parameterized generics

泛型有时允许你将类似参数中的类型传递给函数。这些被称为参数化泛型(或参数多态性)。

¥Generics sometimes allow you to pass types in like arguments to a function. These are known as parameterized generics (or parametric polymorphism).

例如,带有泛型的类型别名被参数化。当你要使用它时,你必须提供一个类型参数。

¥For example, a type alias with a generic is parameterized. When you go to use it you will have to provide a type argument.

1type Item<T> = {2  prop: T,3}4
5let item: Item<string> = {6  prop: "value"7};

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

¥You can think of this like passing arguments to a function, only the return value is a type that you can use.

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

¥Classes (when being used as a type), type aliases, and interfaces all require that you pass type arguments. Functions and function types do not have parameterized generics.

¥Classes

1class Item<T> {2  prop: T;3  constructor(param: T) {4    this.prop = param;5  }6}7
8let item1: Item<number> = new Item(42); // Works!9let item2: Item = new Item(42); // Error!

类型别名

¥Type Aliases

1type Item<T> = {2  prop: T,3};4
5let item1: Item<number> = { prop: 42 }; // Works!6let item2: Item = { prop: 42 }; // Error!

接口

¥Interfaces

1interface HasProp<T> {2  prop: T,3}4
5class Item {6  prop: string;7}8
9Item.prototype as HasProp<string>; // Works!10Item.prototype as HasProp; // Error!

将默认值添加到参数化泛型

¥Adding defaults to parameterized generics

你还可以为参数化泛型提供默认值,就像函数的参数一样。

¥You can also provide defaults for parameterized generics just like parameters of a function.

1type Item<T: number = 1> = {2  prop: T,3};4
5let foo: Item<> = { prop: 1 };6let bar: Item<2> = { prop: 2 };

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

¥You must always include the brackets <> when using the type (just like parentheses for a function call).

差异印记

¥Variance Sigils

你还可以通过方差符号指定泛型的子类型行为。默认情况下,泛型的行为是不变的,但你可以在其声明中添加 + 以使它们表现协变,或在其声明中添加 - 以使它们表现逆变。有关 Flow 方差的更多信息,请参阅 我们关于方差的文档

¥You can also specify the subtyping behavior of a generic via variance sigils. By default, generics behave invariantly, but you may add a + to their declaration to make them behave covariantly, or a - to their declaration to make them behave contravariantly. See our docs on variance for a more information on variance in Flow.

方差符号允许你更具体地了解你打算如何使用泛型,从而使 Flow 能够进行更精确的类型检查。例如,你可能希望保持这种关系:

¥Variance sigils allow you to be more specific about how you intend to use your generics, giving Flow the power to do more precise type checking. For example, you may want this relationship to hold:

1type GenericBox<+T> = T;2
3const x: GenericBox<number> = 3;4x as GenericBox<number| string>;

如果没有 + 方差印记,上面的示例就无法完成:

¥The example above could not be accomplished without the + variance sigil:

1type GenericBoxError<T> = T;2
3const x: GenericBoxError<number> = 3;4x as GenericBoxError<number| string>; // number | string is not compatible with number.

请注意,如果你使用方差标志来注释你的泛型,那么 Flow 将进行检查以确保这些类型仅出现在对该方差标志有意义的位置。例如,你不能将泛型类型参数声明为协变行为并在逆变位置使用它:

¥Note that if you annotate your generic with variance sigils then Flow will check to make sure those types only appear in positions that make sense for that variance sigil. For example, you cannot declare a generic type parameter to behave covariantly and use it in a contravariant position:

1type NotActuallyCovariant<+T> = (T) => void;