マップ型
Flowのマップ型は、オブジェクト型の変換を可能にします。オブジェクトに対する複雑なランタイム操作をモデル化するのに役立ちます。
基本的な使い方
マップ型はインデックス付きオブジェクト型と似た構文を持ちますが、`in`キーワードを使用します。
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`の各キーに置き換えられるため、この型は以下のように評価されます。
{
foo: Methodify<O['foo']>,
bar: Methodify<O['bar']>,
}
= {
foo: () => number,
bar: () => string,
}
マップ型のソース
`in`キーワードの後に来る型をマップ型の*ソース*と呼びます。マップ型のソースは、`string | number | symbol`のサブタイプでなければなりません。
1type MappedType = {[key in boolean]: number}; // ERROR!
1:28-1:34: Cannot instantiate mapped type [1] because boolean [2] is incompatible with `string | number | symbol`, so it cannot be used to generate keys for mapped type [1]. [incompatible-type]
通常は、別のオブジェクト型に基づいてマップ型を作成します。この場合、インライン`keyof`を使用してマップ型を記述する必要があります。
1type GetterOf<T> = () => T;2type Obj = {foo: number, bar: string};3type MappedObj = {[key in keyof Obj]: GetterOf<Obj[key]>};
注:`keyof`は、今のところマップ型でのみインラインで動作します。`keyof`の完全なサポートはまだ利用できません。
しかし、マップ型の生成にオブジェクトを使用する必要はありません。文字列リテラル型の共用体を使用して、オブジェクト型のキーを表すこともできます。
1type Union = 'foo' | 'bar' | 'baz';2type MappedType = {[key in Union]: number};3// = {foo: number, bar: number, baz: number};
重要なのは、文字列リテラルを使用する場合、共用体は*単一のオブジェクト型*に縮約されることです。
1type MappedTypeFromKeys<Keys: string> = {[key in Keys]: number};2type MappedFooAndBar = MappedTypeFromKeys<'foo' | 'bar'>;3// = {foo: number, bar: number}, not {foo: number} | {bar: number}
マップ型のソースで`number`や`string`のような型を使用すると、Flowはインデクサーを作成します。
1type MappedTypeFromKeys<Keys: string> = {[key in Keys]: number};2type MappedFooAndBarWithIndexer = MappedTypeFromKeys<'foo' | 'bar' | string>;3// = {foo: number, bar: number, [string]: number}
分配マップ型
マップ型がインライン`keyof`または`$Keys`によってバインドされた型パラメータを使用する場合、Flowはマップ型をオブジェクト型の共用体に分配します。
例えば
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};
分配マップ型は、`null`と`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
プロパティ修飾子
マップ型では、`+`または`-`の分散修飾子と、オプション修飾子`?`を追加することもできます。
1type O = {foo: number, bar: string}2type ReadOnlyPartialO = {+[key in keyof O]?: O[key]}; // = {+foo?: number, +bar?: string};
分散もオプションも修飾子が指定されておらず、マップ型が分配型の場合、分散とオプションは入力オブジェクトによって決定されます。
1type O = {+foo: number, bar?: string};2type Mapped = {[key in keyof O]: O[key]}; // = {+foo: number, bar?: string}
それ以外の場合、プロパティ修飾子が存在しない場合、プロパティは読み書き可能で必須です。
1type Union = 'foo' | 'bar' | 'baz';2type MappedType = {[key in Union]: number};3// = {foo: number, bar: number, baz: number};
注:Flowは、分散またはオプションの修飾子の削除をまだサポートしていません。
導入
マップ型を使用するには、構文をサポートするようにインフラストラクチャをアップグレードする必要があります。