ユーティリティ型
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
のキーからそれを抽出できました。
enum型を作成する場合は、Flow Enumの方がユースケースに適している場合があります。
$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]
enum型を作成する場合は、Flow Enumの方がユースケースに適している場合があります。
$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.dokyumento.jp/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.dokyumento.jp/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
を使用して、キーのユニオンからオブジェクト型を生成できます。
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;
これは、厳密なオブジェクト型から始めて、オブジェクト型スプレッドを使用して非厳密にすることが、より明確で簡潔であるため、(1つのオブジェクト型の非厳密なバリアントと厳密なバリアントの両方を使用したい場合は)避けるべきユーティリティ型です。
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コンポーネントで受け入れられるpropsの型を定義するために、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>
は、ランタイムオブジェクトの rest 操作を表す型です。例えば、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 操作を表現することを目的としているため、厳密なオブジェクト型は $Rest<A, B>
で異なる扱いを受けるということです。例えば、$Rest<{n: number}, {...}>
は、非厳密な空のオブジェクトが n
プロパティを持つ可能性があるため、{n?: number}
になります。一方、$Diff<{n: number}, {...}>
は {n: number}
になります。
$NonMaybeType<T>
$NonMaybeType<T>
は型 T
を非-maybe型に変換します。言い換えれば、$NonMaybeType<T>
の値は、null
と undefined
を除いた T
の値です。
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>
は、F
が恒等関数型、すなわち <K>(K) => K
の場合の $ObjMapi<Obj, F>
の特殊なケースです。言い換えれば、オブジェクトの各プロパティをプロパティキーの型にマッピングします。$ObjMapi<Obj, <K>(K) => K>
と記述する代わりに、$KeyMirror<Obj>
と記述できます。例:
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]
ヒント: 特定の種類の [invalid-exported-annotation]
エラーを修正するために、(可能であれば) $ObjMapi
の代わりに $KeyMirror
を使用することを推奨します。
$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 から非推奨になりました。代わりに Mapped Types を使用してください。
ObjMap<T, F>
は、オブジェクト型 T
と 関数型 F
を受け取り、オブジェクト内の各値の型を、提供された関数型 F
でマッピングすることによって得られるオブジェクト型を返します。言い換えれば、$ObjMap
は T
のすべてのプロパティ値の型に対して、与えられた関数型 F
を (型レベルで) 呼び出し、それらの呼び出しから得られるオブジェクト型を返します。
例を見てみましょう。入力としてサンク (() => A
の形式の関数) のオブジェクトを受け取る run
という関数があるとします。
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}
関数の目的は、すべてのサンクを実行し、値で構成されたオブジェクトを返すことです。この関数の戻り値の型は何でしょうか。
キーは同じですが、値は異なる型、つまり各関数の戻り値の型を持ちます。値レベル (関数の実装) では、基本的にオブジェクトをマッピングしてキーの新しい値を生成しています。これを型レベルでどのように表現すればよいでしょうか。
ここで 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 から非推奨になりました。代わりに Mapped Types を使用してください。
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 から非推奨になりました。代わりに Mapped Types を使用してください。
$ObjMapConst<Obj, T>
は、F
が定数関数型 (例えば () => T
) の場合の $ObjMap<Obj, F>
の特殊なケースです。$ObjMap<Obj, () => T>
と記述する代わりに、$ObjMapConst<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]
ヒント: 特定の種類の [invalid-exported-annotation]
エラーを修正するために、(可能であれば) $ObjMap
の代わりに $ObjMapConst
を使用することを推奨します。