跳至主要內容

已對應類型

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}

如果您在已對應類型的來源中使用 numberstring 類型,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};

可分配已對應類型也會分配至 nullundefined

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 尚未支援移除變異或可選性修改器。

採用

要使用對應類型,您需要升級您的基礎架構,以支援語法

  • flowflow-parser:0.210.0。在 v0.210.0 至 v0.211.1 之間,您需要在您的 .flowconfig 中明確啟用它,在 [options] 標題下,新增 mapped_type=true
  • prettier: 3
  • babel 搭配 babel-plugin-syntax-hermes-parser。請參閱 我們的 Babel 指南 以取得設定說明。
  • eslint 搭配 hermes-eslint。請參閱 我們的 ESLint 指南 以取得設定說明。