工具類型
Flow 提供一組工具類型,用於操作其他類型以建立新的類型。
$Keys<T>
您可以從物件類型中萃取出金鑰的類型。通常這會是聯合的字串文字類型
1const countries = {2 US: "United States",3 IT: "Italy",4 FR: "France"5};6
7type Country = $Keys<typeof countries>;8
9const italy: Country = 'IT'; // Works10const nope: Country = 'nope'; // Error!
10:23-10:28: Cannot assign `'nope'` to `nope` because property `nope` is missing in object literal [1]. [prop-missing]
在上述範例中,Country
的類型等同於 type Country = 'US' | 'IT' | 'FR'
,但 Flow 能夠從 countries
的金鑰中萃取出它。
如果您想要建立列舉類型,Flow 列舉 可能更適合您的使用案例。
$Values<T>
$Values<T>
代表 物件類型 中所有可列舉屬性的值類型的聯集類型
1type Props = {2 name: string,3 age: number,4};5
6// The following two types are equivalent:7type PropValues = string | number;8type Prop$Values = $Values<Props>;9
10const name: Prop$Values = 'Jon'; // Works11const age: Prop$Values = 42; // Works12const fn: Prop$Values = true; // Error!
12:25-12:28: Cannot assign `true` to `fn` because: [incompatible-type] Either boolean [1] is incompatible with string [2]. Or boolean [1] is incompatible with number [3].
請注意,在物件文字的 typeof
上使用 $Values
會產生比您預期的更通用的類型
1const obj = {2 foo: 1,3 bar: 2,4};5
6function acceptsValues(x: $Values<typeof obj>) { /* ... */ }7
8acceptsValues(1); // Works9acceptsValues(3); // Works, because the type was interpreted as `number`.
如果您在物件文字表達式上使用 Object.freeze
,此行為會改變
1const obj = Object.freeze({2 foo: 1,3 bar: 2,4});5
6function acceptsValues(x: $Values<typeof obj>) { /* ... */ }7
8acceptsValues(1); // Works9acceptsValues(3); // Error! Because the type was interpreted as `1 | 2`.
9:15-9:15: Cannot call `acceptsValues` with `3` bound to `x` because number [1] is incompatible with literal union [2]. [incompatible-call]
如果您想要建立列舉類型,Flow 列舉 可能更適合您的使用案例。
$ReadOnly<T>
$ReadOnly<T>
是一個類型,代表給定 物件類型 或 元組類型 T
的唯讀版本(元組支援適用於 Flow ≥0.212)。唯讀物件類型是一個物件類型,其所有金鑰都是 唯讀 的。類似地,唯讀元組是一個每個元素都是 唯讀 的元組。
這表示下列內容是等效的
1type ReadOnlyObj = {2 +key: number, // read-only field, marked by the `+` annotation3};4type ReadOnlyTuple = [+foo: number];
→
1type ReadOnlyObj = $ReadOnly<{2 key: number,3}>;4type ReadOnlyTuple = $ReadOnly<[number]>;
當您需要使用已定義物件類型的唯讀版本時,這很有用,而無需手動重新定義和註解每個金鑰為唯讀。例如
1type Props = {2 name: string,3 age: number,4};5
6type ReadOnlyProps = $ReadOnly<Props>;7
8function render(props: ReadOnlyProps) {9 const {name, age} = props; // Works10 props.age = 42; // Error! 11}
10:9-10:11: Cannot assign `42` to `props.age` because property `age` is not writable. [cannot-write]
此外,其他公用程式類型(例如 $ObjMap<T>
)可能會移除任何讀寫註解,因此 $ReadOnly<T>
是在對物件進行操作後快速再次將物件設為唯讀的便捷方式
1type Obj = {2 +key: number,3};4
5type MappedObj = $ReadOnly<$ObjMap<Obj, <T>(T) => Array<T>>> // Still read-only
$ReadOnly
公用程式用於物件和元組類型。如果您想讓其他類型變為唯讀,可以使用下列其中一項
Array<T>
->$ReadOnlyArray<T>
Set<T>
->$ReadOnlySet<T>
Map<K, V>
->$ReadOnlyMap<K, V>
WeakSet<T>
->$ReadOnlyWeakSet<T>
WeakMap<K, V>
->$ReadOnlyWeakMap<K, V>
Partial<T>
≥0.201
此公用程式會將物件或介面的所有命名欄位轉換為選用欄位,同時保留物件的所有其他屬性(例如精確性、變異性)。請使用此公用程式,而不是 $Shape
。
自 Flow ≥0.212 以來,它也會將元組類型的所有元素轉換為 選用 元素。
物件範例
1type Person = {2 name: string,3 age: number,4};5type PartialPerson = Partial<Person>;6// Above equivalent to `{name?: string, age?: number}`7
8const a: PartialPerson = {}; // OK9const b: PartialPerson = {name: 'George'}; // OK10const c: PartialPerson = {name: 'George', age: 123}; // OK11
12c as Person; // ERROR: `PersonDetails` is not a `Person` (unlike with `$Shape`)
12:1-12:1: Cannot cast `c` to `Person` because undefined [1] is incompatible with number [1] in property `age`. [incompatible-cast]12:1-12:1: Cannot cast `c` to `Person` because undefined [1] is incompatible with string [1] in property `name`. [incompatible-cast]
元組範例
1type AllRequired = [number, string];2[] as Partial<AllRequired>; // OK: like `[a?: number, b?: string]` now
由於可變性,無法將類型為 T
的物件或元組提供給 Partial<T>
。您可以透過將物件設為 唯讀 來解決此問題。
1type Person = {2 name: string,3 age: number,4};5
6const person: Person = {name: 'George', age: 123};7
8function noPerson(o: Partial<Person>) {9 // Can mutate:10 o.name = undefined;11}12noPerson(person); // Error! 13
14function okPerson(o: $ReadOnly<Partial<Person>>) {15 // Can't mutate - it's read-only!16}17okPerson(person); // Works
12:10-12:15: Cannot call `noPerson` with `person` bound to `o` because undefined [1] is incompatible with number [1] in property `age`. This property is invariantly typed. See https://flow.dev.org.tw/en/docs/faq/#why-cant-i-pass-a-string-to-a-function-that-takes-a-string-number. [incompatible-call]12:10-12:15: Cannot call `noPerson` with `person` bound to `o` because undefined [1] is incompatible with string [1] in property `name`. This property is invariantly typed. See https://flow.dev.org.tw/en/docs/faq/#why-cant-i-pass-a-string-to-a-function-that-takes-a-string-number. [incompatible-call]
注意:在 Flow 版本 0.201 之前,此公用程式類型稱為 $Partial
。
Required<T>
≥0.201
Required
公用程式類型與 Partial
相反:它會將物件或介面的所有選用欄位轉換為必要欄位。
自 Flow ≥0.212 以來,它也會將元組類型的所有元素轉換為 必要 元素。
物件範例
1type PartialPerson = {2 name?: string,3 age?: number,4};5type Person = Required<PartialPerson>;6// Above equivalent to `{name: string, age: number}`7
8const a: Person = {name: 'George', age: 123}; // OK9const b: Person = {age: 123}; // ERROR: missing `name` property
9:19-9:28: Cannot assign object literal to `b` because property `name` is missing in object literal [1] but exists in `PartialPerson` [2]. [prop-missing]
元組範例
1type AllOptional = [a?: number, b?: string];2[] as Required<AllOptional>; // ERROR: like `[a: number, b: string]` now
2:1-2:2: Cannot cast array literal to required of `AllOptional` because empty array literal [1] has 0 elements but `AllOptional` [2] has 2 elements. [invalid-tuple-arity]
ReturnType<F>
≥0.209
此公用程式類型會從指定的函式類型中萃取回傳類型。
1declare function f(s: string, n: number): boolean;2type Bool = ReturnType<typeof f>;3true as Bool;41 as Bool; // Error: number is not boolean
4:1-4:1: Cannot cast `1` to `Bool` because number [1] is incompatible with boolean [2]. [incompatible-cast]
Parameters<F>
≥0.209
此公用程式類型會將指定函式類型中的參數類型萃取至 元組類型 中。
1declare function f(s: string, n: number): boolean;2type Tuple = Parameters<typeof f>; // Evaluates to [string, number]3's' as Tuple[0];41 as Tuple[1];5false as Tuple[2]; // Error: tuple type only has two elements
5:16-5:16: Cannot access number literal `2` on `Tuple` because tuple type [1] only has 2 elements, so index 2 is out of bounds. [invalid-tuple-index]
Exclude<T, U>
≥0.209
此公用程式類型會從 T
中排除 U
的所有子類型。
1type T = Exclude<1 | 2 | 3 | 4, 1 | 3>; // evaluates to 2 | 421 as T; // error 32 as T; // ok43 as T; // error 54 as T; // ok
2:1-2:1: Cannot cast `1` to `T` because number [1] is incompatible with literal union [2]. [incompatible-cast]4:1-4:1: Cannot cast `3` to `T` because number [1] is incompatible with literal union [2]. [incompatible-cast]
Extract<T, U>
≥0.209
此公用程式類型只保留 T
中 U
的子類型。
1declare class Car {}2declare class Animal {}3declare class Dog extends Animal {}4declare class Cat extends Animal {}5type T = Extract<Car | Dog | Cat, Animal>; // evaluates to Dog | Cat6new Car() as T; // error 7new Dog() as T; // ok8new Cat() as T; // ok
6:1-6:9: Cannot cast `new Car()` to `T` because: [incompatible-cast] Either `Car` [1] is incompatible with `Dog` [2]. Or `Car` [1] is incompatible with `Cat` [3].
ThisParameterType<F>
≥0.209
此公用程式類型會萃取指定函式類型中 this
參數的類型。
1type T = ThisParameterType<(this: number, bar: string) => void>; // Evaluates to number2'1' as T; // error 32 as T; // ok
2:1-2:3: Cannot cast `'1'` to `T` because string [1] is incompatible with number [2]. [incompatible-cast]
OmitThisParameter<F>
≥0.209
此公用程式類型會從指定的函式類型中移除 this
參數。
1type HasThisParamFun = (this: number, bar: string) => void;2type NoThisParamFun = OmitThisParameter<HasThisParamFun> // Evaluates to (bar: string) => void3declare const hasThisParam: HasThisParamFun;4declare const noThisParam: NoThisParamFun;5
6hasThisParam(''); // error: global object is not number 7noThisParam(''); // ok: no this type requirement
6:1-6:12: Cannot call `hasThisParam` because global object [1] is incompatible with number [2]. [incompatible-call]
Pick<O, Keys>
≥0.211
此公用程式類型允許您使用另一個物件類型的子欄位集合來產生一個物件類型。
1type O = {foo: number, bar: string, baz: boolean};2type FooAndBar = Pick<O, 'foo' | 'bar'>;3
4declare const fooAndBar: FooAndBar;5fooAndBar.baz; // error: baz is missing 6fooAndBar.foo as number; // ok7fooAndBar.bar as string; // ok
5:11-5:13: Cannot get `fooAndBar.baz` because property `baz` (did you mean `bar`?) is missing in `Pick` [1]. [prop-missing]
Omit<O, Keys>
≥0.211
此工具類型允許您透過省略其他物件類型中的指定欄位來產生一個物件類型。
1type O = {foo: number, bar: string, baz: boolean};2type JustBaz= Omit<O, 'foo' | 'bar'>;3
4declare const justBaz: JustBaz;5justBaz.baz as boolean; // ok6justBaz.foo; // error: missing foo 7justBaz.bar; // error: missing bar
6:9-6:11: Cannot get `justBaz.foo` because property `foo` is missing in `Pick` [1]. [prop-missing]7:9-7:11: Cannot get `justBaz.bar` because property `bar` (did you mean `baz`?) is missing in `Pick` [1]. [prop-missing]
Record<Keys, Type>
≥0.211
此工具類型允許您從具有給定 Type
的鍵聯合產生一個物件類型,每個欄位都具有該 Type
。
1type NumberRecord = Record<'foo' | 'bar', number>;2declare const numberRecord: NumberRecord;3numberRecord.foo as number; // ok4numberRecord.bar as number; // ok5numberRecord.baz; // error
5:14-5:16: Cannot get `numberRecord.baz` because property `baz` (did you mean `bar`?) is missing in `Record` [1]. [prop-missing]
請注意 Record
與使用索引器不同
1type NumberRecord = Record<'foo' | 'bar', number>;2type IndexedObject = {['foo' | 'bar']: number};3
4// Record uses explicit fields, which means they are all required5const rec: Record = {}; // error 6// Indexers do not have this same requirement7const idx: IndexedObject = {}; // no error
5:12-5:17: Cannot use `Record` [1] without 2 type arguments. [missing-type-arg]
$Exact<T>
您可以使用 $Exact
來讓一個不精確物件類型變為精確
1type InexactUser = {name: string, ...};2type ExactUser = $Exact<InexactUser>;3
4const user = {name: 'John Wilkes Booth'};5// These will both be satisfied because they are equivalent:6const a: ExactUser = user;7const b: {name: string} = user;
這是一個應避免使用的工具類型,因為一開始就使用精確物件類型並使用物件類型散佈讓它變成不精確(如果您希望同時擁有不精確和精確的其中一個物件類型變體)會更清楚且更簡潔
1type ExactUser = {name: string};2type InexactUser = {...ExactUser, ...};3
4const user = {name: 'John Wilkes Booth'};5const a: ExactUser = user;
$Diff<A, B>
正如名稱所暗示的,$Diff<A, B>
是代表 A
和 B
的集合差的類型,亦即 A \ B
,其中 A
和 B
都是物件類型。以下是一個範例
1type Props = {name: string, age: number, ...};2type DefaultProps = {age: number};3type RequiredProps = $Diff<Props, DefaultProps>;4
5function setProps(props: RequiredProps) {6 // ...7}8
9setProps({name: 'foo'}); // Works10setProps({name: 'foo', age: 42, baz: false}); // Works, you can pass extra props too11setProps({age: 42}); // Error! `name` is required
11:10-11:18: Cannot call `setProps` with object literal bound to `props` because property `name` is missing in object literal [1] but exists in `Props` [2]. [prop-missing]
您可能已經注意到,這個範例並非隨機範例。$Diff
正是 React 定義檔用來定義 React 元件所接受的道具類型的內容。
請注意,如果您移除屬性的物件沒有您要移除的屬性,則 $Diff<A, B>
會出錯,亦即如果 B
有 A
中不存在的鍵
1type Props = {name: string, age: number};2type DefaultProps = {age: number, other: string};3type RequiredProps = $Diff<Props, DefaultProps>; // Error! 4
5function setProps(props: RequiredProps) {6 props.name;7 // ...8}
3:28-3:32: Cannot instantiate `$Diff` because undefined property `other` [1] is incompatible with string [2]. [incompatible-type]
作為解決方法,您可以將 A
中不存在的屬性指定為選用。例如
1type A = $Diff<{}, {nope: number}>; // Error! 2type B = $Diff<{}, {nope: number | void}>; // Works3
4const a: A = {};5const b: B = {};
1:16-1:17: Cannot instantiate `$Diff` because undefined property `nope` [1] is incompatible with number [2]. [incompatible-type]
$Rest<A, B>
$Rest<A, B>
是表示執行時期物件剩餘運算的類型,例如:const {foo, ...rest} = obj
,其中 A
和 B
都是 物件類型。此運算產生的類型將會是包含 A
的自有屬性(不是 B
中的自有屬性)的物件類型。在 Flow 中,我們將 精確物件類型 上的所有屬性都視為 自有 屬性。對於不精確物件,屬性可能是自有的,也可能不是自有的。
例如
1type Props = {name: string, age: number};2
3const props: Props = {name: 'Jon', age: 42};4const {age, ...otherProps} = props;5otherProps as $Rest<Props, {age: number}>;6otherProps.age; // Error!
6:12-6:14: Cannot get `otherProps.age` because property `age` is missing in rest of object pattern [1]. [prop-missing]
與 $Diff<A, B>
的主要差異在於,$Rest<A, B>
旨在表示真實的執行時期剩餘運算,這表示精確物件類型在 $Rest<A, B>
中的處理方式不同。例如,$Rest<{n: number}, {...}>
會產生 {n?: number}
,因為不精確的空物件可能有一個 n
屬性,而 $Diff<{n: number}, {...}>
會產生 {n: number}
。
$NonMaybeType<T>
$NonMaybeType<T>
會將類型 T
轉換為非 Maybe 類型。換句話說,$NonMaybeType<T>
的值就是 T
的值,但排除 null
和 undefined
。
1type MaybeName = ?string;2type Name = $NonMaybeType<MaybeName>;3
4'Gabriel' as MaybeName; // Works5null as MaybeName; // Works6'Gabriel' as Name; // Works7null as Name; // Error! `null` can't be annotated as Name because Name is not a maybe type
7:1-7:4: Cannot cast `null` to `Name` because null [1] is incompatible with string [2]. [incompatible-cast]
$KeyMirror<O>
$KeyMirror<Obj>
是 $ObjMapi<Obj, F>
的特殊情況,其中 F
是身分函數類型,即 <K>(K) => K
。換句話說,它會將物件的每個屬性都對應到屬性金鑰的類型。您可以寫 $KeyMirror<Obj>
,而不是寫 $ObjMapi<Obj, <K>(K) => K>
。例如
1const obj = {2 a: true,3 b: 'foo'4};5
6declare function run<O: {...}>(o: O): $KeyMirror<O>;7
8// newObj is of type {a: 'a', b: 'b'}9const newObj = run(obj);10
11newObj.a as 'a'; // Works12newObj.b as 'a'; // Error! String 'b' is incompatible with 'a'
12:1-12:8: Cannot cast `newObj.b` to string literal `a` because string literal `b` [1] is incompatible with string literal `a` [2]. [incompatible-cast]
提示:如果可以的話,優先使用 $KeyMirror
而不是 $ObjMapi
,以修正特定類型的 [invalid-exported-annotation]
錯誤。
$TupleMap<T, F>
$TupleMap<T, F>
會採用一個可迭代類型 T
(例如:Tuple
或 Array
)和一個 函數類型 F
,然後傳回可迭代類型,該類型是透過將可迭代中每個值的類型對應到提供的函數類型 F
而取得的。這類似於 JavaScript 函數 map
。
遵循我們在 $ObjMap<T>
中的範例,假設 run
會採用一個函式陣列,而不是物件,並對它們進行對應,傳回一個函式呼叫結果的陣列。我們可以像這樣註解它的回傳類型
1// Function type that takes a `() => V` and returns a `V` (its return type)2type ExtractReturnType = <V>(() => V) => V3
4function run<A, I: Array<() => A>>(iter: I): $TupleMap<I, ExtractReturnType> {5 return iter.map(fn => fn());6}7
8const arr = [() => 'foo', () => 'bar'];9run(arr)[0] as string; // Works10run(arr)[1] as string; // Works11run(arr)[1] as boolean; // Error!
11:1-11:11: Cannot cast `run(...)[1]` to boolean because string [1] is incompatible with boolean [2]. [incompatible-cast]
Class<T>
給定一個代表類別 C
的實例的類型 T
,類型 Class<T>
是類別 C
的類型。例如
1class Store {}2class ExtendedStore extends Store {}3class Model {}4
5function makeStore(storeClass: Class<Store>) {6 return new storeClass();7}8
9makeStore(Store) as Store;10makeStore(ExtendedStore) as Store;11makeStore(Model) as Model; // Error!
11:1-11:16: Cannot cast `makeStore(...)` to `Model` because `Store` [1] is incompatible with `Model` [2]. [incompatible-cast]11:11-11:15: Cannot call `makeStore` with `Model` bound to `storeClass` because `Model` [1] is incompatible with `Store` [2]. [incompatible-call]
對於採用類型參數的類別,您也必須提供參數。例如
1class ParamStore<T> {2 constructor(data: T) {}3}4
5function makeParamStore<T>(storeClass: Class<ParamStore<T>>, data: T): ParamStore<T> {6 return new storeClass(data);7}8makeParamStore(ParamStore, 1) as ParamStore<number>;9makeParamStore(ParamStore, 1) as ParamStore<boolean>; // Error!
9:28-9:28: Cannot call `makeParamStore` with `1` bound to `data` because number [1] is incompatible with boolean [2]. [incompatible-call]
$Exports<T>
下列功能等效
import typeof * as T from 'my-module';
type T = $Exports<'my-module'>;
$Exports
語法的優點是您可以在同一行上 export
類型
export type T = $Exports<'my-module'>;
而您在其他情況下需要在 import typeof
案例中匯出別名
import typeof * as T from 'my-module';
export type MyModuleType = T;
已棄用的公用程式類型
$PropertyType<T, k>
警告: $PropertyType
已於 Flow 版本 0.155 中棄用,並將在未來的 Flow 版本中移除。
$PropertyType<T, 'k'>
等同於 T['k']
索引存取類型。
$ElementType<T, K>
警告: $ElementType
已於 Flow 版本 0.155 中棄用,並將在未來的 Flow 版本中移除。
$ElementType<T, K>
等同於 T[K]
索引存取類型。
$Partial
≤0.202
Partial 的前別名。支援已於版本 0.203 中移除。
$Shape<T>
注意:已棄用!此公用程式不安全 - 請使用上面說明的 Partial
,讓物件的所有欄位都變成選用。
$Shape<T>
類型的變數,其中 T
是某個物件類型,可以指定包含 T
中包含的屬性子集的物件 o
。對於 T
的每個屬性 p: S
,o
中 p
的潛在繫結類型必須與 S
相容。
例如
1type Person = {2 age: number,3 name: string,4};5// $FlowIgnore[deprecated-utility]6type PersonDetails = $Shape<Person>;7
8const person1: Person = {age: 28}; // ERROR: missing `name` 9const person2: Person = {name: 'a'}; // ERROR: missing `age` 10const person3: PersonDetails = {age: 28}; // OK11const person4: PersonDetails = {name: 'a'}; // OK12const person5: PersonDetails = {age: 28, name: 'a'}; // OK13const person6: PersonDetails = {age: '28'}; // ERROR: string is incompatible with number
8:25-8:33: Cannot assign object literal to `person1` because property `name` is missing in object literal [1] but exists in `Person` [2]. [prop-missing]9:25-9:35: Cannot assign object literal to `person2` because property `age` is missing in object literal [1] but exists in `Person` [2]. [prop-missing]
注意:$Shape<T>
不等於所有欄位標記為選用的 T
。特別是,Flow 不合理地允許 $Shape<T>
在多個內容中用作 T
。例如
const personShape: PersonDetails = {age: 28};
personShape as Person;
Flow 將不合理地允許最後一次轉型成功。
在某些內容中,它也不等於它自己
function f<T>(input: $Shape<T>): $Shape<T> {
return input; // ERROR: `T` is incompatible with `$Shape` of `T`
}
此實用程式類型已過時,未來將刪除 - 請改用 Partial
。
$Call<F, T...>
注意:已過時!此實用程式自 Flow 版本 0.208 起已過時 - 請改用 條件類型 或 索引存取類型 來擷取類型。
$Call<F, T...>
是一種類型,用於表示呼叫給定 函式類型 F
的結果,其中有 0 個或更多個引數 T...
。這類似於在執行時期呼叫函式(更具體地說,它類似於呼叫 Function.prototype.call
),但位於類型層級;這表示函式類型呼叫會靜態發生,亦即不會在執行時期發生。
讓我們看幾個範例
1// Takes an object type, returns the type of its `prop` key2type ExtractPropType = <T>({prop: T, ...}) => T;3type Obj = {prop: number};4type PropType = $Call<ExtractPropType, Obj>; // Call `ExtractPropType` with `Obj` as an argument5type Nope = $Call<ExtractPropType, {nope: number}>; // Error! Argument doesn't match `Obj`. 6
75 as PropType; // Works8true as PropType; // Error! PropType is a number
5:36-5:49: Cannot instantiate `$Call` because property `prop` is missing in object type [1] but exists in object type [2] in the first argument. [prop-missing]8:1-8:4: Cannot cast `true` to `PropType` because boolean [1] is incompatible with number [2]. [incompatible-cast]
1// Takes a function type, and returns its return type2type ExtractReturnType = <R>(() => R) => R;3type Fn = () => number;4type ReturnType = $Call<ExtractReturnType, Fn>;5
65 as ReturnType; // Works7true as ReturnType; // Error! ReturnType is a number
7:1-7:4: Cannot cast `true` to `ReturnType` because boolean [1] is incompatible with number [2]. [incompatible-cast]
$Call
可能非常強大,因為它允許您在類型層級中進行呼叫,否則您必須在執行時期進行。類型層級呼叫會靜態發生,並會在執行時期清除。
1// Getting return types:2function getFirstValue<V>(map: Map<string, V>): ?V {3 for (const [key, value] of map.entries()) {4 return value;5 }6 return null;7}8
9// Using $Call, we can get the actual return type of the function above:10type Value = $Call<typeof getFirstValue, Map<string, number>>;11
125 as Value;13true as Value; // Error! Value is a `number` 14
15// We could generalize it further:16type GetMapValue<M> =17 $Call<typeof getFirstValue, M>;18
195 as GetMapValue<Map<string, number>>;20true as GetMapValue<Map<string, boolean>>;21true as GetMapValue<Map<string, number>>; // Error! value is a `number`
13:1-13:4: Cannot cast `true` to `Value` because boolean [1] is incompatible with number [2]. [incompatible-cast]21:1-21:4: Cannot cast `true` to `GetMapValue` because boolean [1] is incompatible with number [2]. [incompatible-cast]
$ObjMap<T, F>
注意:已過時!此實用程式自 Flow 版本 0.211 起已過時 - 請改用 對應類型。
ObjMap<T, F>
會取得 物件類型 T
和 函式類型 F
,並傳回透過使用提供的函式類型 F
對應物件中每個值類型而取得的物件類型。換句話說,$ObjMap
會(在類型層級)對 T
中每個屬性值類型呼叫 (at the type level) 給定的函式類型 F
,並從這些呼叫傳回結果物件類型。
讓我們看一個範例。假設你有一個名為 run
的函式,它接收一個 thunk 的物件(函式形式為 () => A
)作為輸入
1function run<O: {[key: string]: (...$ReadOnlyArray<mixed>) => mixed}>(o: O): $FlowFixMe {2 return Object.keys(o).reduce<{[string]: (...$ReadOnlyArray<mixed>) => mixed}>(3 (acc, k) => ({...acc, [(k: string)]: o[k]()}),4 {},5 );6}
這個函式的目的是執行所有 thunk 並傳回一個由值組成的物件。這個函式的傳回類型是什麼?
鍵相同,但值有不同的類型,也就是每個函式的傳回類型。在值層級(函式的實作),我們基本上會對物件進行對應,以產生鍵的新值。如何在類型層級表達這一點?
這時 ObjMap<T, F>
就派上用場了
1// let's write a function type that takes a `() => V` and returns a `V` (its return type)2type ExtractReturnType = <V>(() => V) => V;3
4declare function run<O: {[key: string]: (...$ReadOnlyArray<mixed>) => mixed}>(o: O): $ObjMap<O, ExtractReturnType>;5
6const o = {7 a: () => true,8 b: () => 'foo'9};10
11run(o).a as boolean; // Works12run(o).b as string; // Works13run(o).b as boolean; // Error! `b` is a string 14run(o).c; // Error! `c` was not in the original object
13:1-13:8: Cannot cast `run(...).b` to boolean because string [1] is incompatible with boolean [2]. [incompatible-cast]14:8-14:8: Cannot get `run(...).c` because property `c` is missing in object type [1]. [prop-missing]
這對於表達處理物件值的函式的傳回類型非常有用。你可以使用類似的做法(例如)提供 bluebird 的 Promise.props
函式的傳回類型,它就像 Promise.all
,但接收物件作為輸入。
以下是這個函式的可能宣告,它與我們的第一個範例非常類似
1declare function props<A, O: {[key: string]: A}>(promises: O): Promise<$ObjMap<O, typeof $await>>;2
3const promises = {a: Promise.resolve(42)};4props(promises).then(o => {5 o.a as 42; // Works6 o.a as 43; // Error! Flow knows it's 42 7});
6:3-6:5: Cannot cast `o.a` to number literal `43` because number [1] is incompatible with number literal `43` [2]. [incompatible-cast]
$ObjMapi<T, F>
注意:已過時!此實用程式自 Flow 版本 0.211 起已過時 - 請改用 對應類型。
ObjMapi<T, F>
類似於 ObjMap<T, F>
。不同之處在於函式類型 F
會使用物件類型 T
的元素的鍵和值類型(而不仅仅是值類型)來 呼叫。例如
1const o = {2 a: () => true,3 b: () => 'foo'4};5
6type ExtractReturnObjectType = <K, V>(K, () => V) => { k: K, v: V };7
8declare function run<O: {...}>(o: O): $ObjMapi<O, ExtractReturnObjectType>;9
10run(o).a as {k: 'a', v: boolean}; // Works11run(o).b as {k: 'b', v: string}; // Works12run(o).a as {k: 'b', v: boolean}; // Error! `a.k` is "a" 13run(o).b as {k: 'b', v: number}; // Error! `b.v` is a string 14run(o).c; // Error! `c` was not in the original object
12:1-12:8: Cannot cast `run(...).a` to object type because string literal `a` [1] is incompatible with string literal `b` [2] in property `k`. [incompatible-cast]13:1-13:8: Cannot cast `run(...).b` to object type because string [1] is incompatible with number [2] in property `v`. [incompatible-cast]14:8-14:8: Cannot get `run(...).c` because property `c` is missing in object type [1]. [prop-missing]
$ObjMapConst<O, T>
注意:已過時!此實用程式自 Flow 版本 0.211 起已過時 - 請改用 對應類型。
$ObjMapConst<Obj, T>
是 $ObjMap<Obj, F>
的特殊情況,其中 F
是常數函式類型,例如 () => T
。你可以寫 $ObjMapConst<Obj, T>
,而不是寫 $ObjMap<Obj, () => T>
。例如
1const obj = {2 a: true,3 b: 'foo'4};5
6declare function run<O: {...}>(o: O): $ObjMapConst<O, number>;7
8// newObj is of type {a: number, b: number}9const newObj = run(obj);10
11newObj.a as number; // Works12newObj.b as string; // Error! Property `b` is a number
12:1-12:8: Cannot cast `newObj.b` to string because number [1] is incompatible with string [2]. [incompatible-cast]
提示:優先使用 $ObjMapConst
而不是 $ObjMap
(如果可能)來修正特定類型的 [invalid-exported-annotation]
錯誤。