Skip to main content

映射类型

Flow 的映射类型允许你转换对象类型。它们对于对对象上的复杂运行时操作进行建模非常有用。

¥Flow's mapped types allow you to transform object types. They are useful for modeling complex runtime operations over objects.

基本用法

¥Basic Usage

映射类型的语法与索引对象类型类似,但使用 in 关键字:

¥Mapped Types have syntax similar to indexed object types but use the in keyword:

1type O = {foo: number, bar: string};2
3type Methodify<T> = () => T;4
5type MappedType = {[key in keyof O]: Methodify<O[key]>};

在此示例中,MappedType 具有 O 中的所有键以及由 Methoditfy<O[key]> 转换的所有值类型。创建属性时,key 变量会替换 O 中的每个键,因此该类型的计算结果为:

¥In this example, MappedType has all of the keys from O with all of the value types transformed by Methoditfy<O[key]>. The key variable is substituted for each key in O when creating the property, so this type evaluates to:

{
foo: Methodify<O['foo']>,
bar: Methodify<O['bar']>,
}
= {
foo: () => number,
bar: () => string,
}

映射类型源

¥Mapped Type Sources

我们将 in 关键字后面的类型称为映射类型的源。映射类型的源必须是 string | number | symbol 的子类型:

¥We call the type that comes after the in keyword the source of the mapped type. The source of a mapped type must be a subtype of string | number | symbol:

1type MappedType = {[key in boolean]: number}; // ERROR!

通常,你需要基于另一个对象类型创建映射类型。在这种情况下,你应该使用内联 keyof 编写映射类型:

¥Typically, you'll want to create a mapped type based on another object type. In this case, you should write your mapped type using an inline keyof:

1type GetterOf<T> = () => T;2type Obj = {foo: number, bar: string};3type MappedObj = {[key in keyof Obj]: GetterOf<Obj[key]>};

注意:keyof 目前仅在映射类型中内联工作。尚未提供对 keyof 的全面支持。

¥NOTE: keyof only works inline in mapped types for now. Full support for keyof is not yet available.

但你不需要使用对象来生成映射类型。你还可以使用字符串字面量类型的联合来表示对象类型的键:

¥But you do not need to use an object to generate a mapped type. You can also use a union of string literal types to represent the keys of an object type:

1type Union = 'foo' | 'bar' | 'baz';2type MappedType = {[key in Union]: number};3// = {foo: number, bar: number, baz: number};

重要的是,当使用字符串字面量时,联合会折叠为单个对象类型:

¥Importantly, when using string literals the union is collapsed into a single object type:

1type MappedTypeFromKeys<Keys: string> = {[key in Keys]: number};2type MappedFooAndBar = MappedTypeFromKeys<'foo' | 'bar'>;3// = {foo: number, bar: number}, not {foo: number} | {bar: number}

如果你在映射类型的源中使用像 numberstring 这样的类型,那么 Flow 将创建一个索引器:

¥If you use a type like number or string in the source of your mapped type then Flow will create an indexer:

1type MappedTypeFromKeys<Keys: string> = {[key in Keys]: number};2type MappedFooAndBarWithIndexer = MappedTypeFromKeys<'foo' | 'bar' | string>;3// = {foo: number, bar: number, [string]: number}

分配映射类型

¥Distributive Mapped Types

当映射类型使用内联 keyof 或由 $Keys 绑定的类型参数时,Flow 会将映射类型分布在对象类型的联合上。

¥When the mapped type uses an inline keyof or a type parameter bound by a $Keys Flow will distribute the mapped type over unions of object types.

例如:

¥For example:

1// This mapped type uses keyof inline2type MakeAllValuesNumber<O: {...}> = {[key in keyof O]: number};3type ObjWithFoo = {foo: string};4type ObjWithBar = {bar: string};5
6type DistributedMappedType = MakeAllValuesNumber<7  | ObjWithFoo8  | ObjWithBar9>; // = {foo: number} | {bar: number};10
11// This mapped type uses a type parameter bound by $Keys12type Pick<O: {...}, Keys: $Keys<O>> = {[key in Keys]: O[key]};13type O1 = {foo: number, bar: number};14type O2 = {bar: string, baz: number};15type PickBar = Pick<O1 | O2, 'bar'>; // = {bar: number} | {bar: string};

分布式映射类型也将分布在 nullundefined 上:

¥Distributive mapped types will also distribute over null and undefined:

1type Distributive<O: ?{...}> = {[key in keyof O]: O[key]};2type Obj = {foo: number};3type MaybeMapped = Distributive<?Obj>;// = ?{foo: number}4null as MaybeMapped; // OK5undefined as MaybeMapped; // OK6({foo: 3}) as MaybeMapped; // OK

属性修饰符

¥Property Modifiers

你还可以在映射类型中添加 +- 方差修饰符以及可选修饰符 ?

¥You can also add + or - variance modifiers and the optionality modifier ? in mapped types:

1type O = {foo: number, bar: string}2type ReadOnlyPartialO = {+[key in keyof O]?: O[key]}; // = {+foo?: number, +bar?: string};

当未提供方差或可选性修饰符并且映射类型是分布式时,方差和可选性由输入对象确定:

¥When no variance nor optionality modifiers are provided and the mapped type is distributive, the variance and optionality are determined by the input object:

1type O = {+foo: number, bar?: string};2type Mapped = {[key in keyof O]: O[key]}; // = {+foo: number, bar?: string}

否则,当不存在属性修饰符时,这些属性是可读写的并且是必需的:

¥Otherwise, the properties are read-write and required when no property modifiers are present:

1type Union = 'foo' | 'bar' | 'baz';2type MappedType = {[key in Union]: number};3// = {foo: number, bar: number, baz: number};

注意:Flow 尚不支持删除方差或可选性修饰符。

¥NOTE: Flow does not yet support removing variance or optionality modifiers.

数组上的映射类型

¥Mapped Type on Arrays

0.246

{#toc-mapped-type-on-arrays}

映射类型也适用于数组或元组输入。如果映射类型的形式为

¥Mapped type also works on array or tuple inputs. If the mapped type is in the form of

{[K in keyof <type_1>]: <type_2>}

然后 type_1 可以是数组或元组类型。

¥then type_1 is allowed to be an array or tuple type.

如果你想映射元组的元素,此功能将特别有用:

¥This feature will be especially useful if you want to map over elements of a tuple:

1type Tuple = [+a: number, b?: string];2type MappedTuple = {[K in keyof Tuple]: Tuple[K] extends number ? boolean : string};3const a: MappedTuple[0] = true;4const b: MappedTuple[1] = '';5'' as MappedTuple[0] // error6false as MappedTuple[1] // error7declare const mapped: MappedTuple;8mapped[0] = true; // error: cannot-write

目前,数组输入上唯一支持的属性修饰符是可选修饰符 ?

¥For now, the only supported property modifier on array input is the optionality modifier ?.

1type Tuple = [+a: number, b?: string];2type Supported = {[K in keyof Tuple]?: string};3type Unsupported1 = {+[K in keyof Tuple]: string};4type Unsupported2 = {-[K in keyof Tuple]: string};

采纳

¥Adoption

要使用映射类型,你需要升级基础架构以使其支持语法:

¥To use mapped types, you need to upgrade your infrastructure so that it supports the syntax:

  • flowflow-parser:0.210.0.在 v0.210.0 到 v0.211.1 之间,你需要在 .flowconfig 中显式启用它,在 [options] 标题下添加 mapped_type=true

    ¥flow and flow-parser: 0.210.0. Between v0.210.0 to v0.211.1, you need to explicitly enable it in your .flowconfig, under the [options] heading, add mapped_type=true.

  • prettier:3

  • 使用 babel-plugin-syntax-hermes-parser babel。请参阅 我们的 Babel 指南 了解设置说明。

    ¥babel with babel-plugin-syntax-hermes-parser. See our Babel guide for setup instructions.

  • 使用 hermes-eslint eslint。请参阅 我们的 ESLint 指南 了解设置说明。

    ¥eslint with hermes-eslint. See our ESLint guide for setup instructions.