この記事ではflowtypeのv0.50.0からv0.59.0にかけて追加されたUtility Typeについて記載しています。
それ以前のバージョンで追加されたUtility Typeについては下記を参照してください。
$ElementType
$ElementType
はT
にObject
を指定して、string
にそのキーの文字列を渡すと対応した値の型を返します。
これだけ聞くと$PropertyTypeと同じように感じますが、$PropertyType
ではキーにString Literal
のみの指定だったのが、$ElementType
ではキーにString
を指定できるため適用できる範囲が広くなりました。
declare function getObjectValue<O: Object, K>(obj: O, key: K): $ElementType<O, K>;
type Obj = {
a: number;
b: string;
c: boolean;
};
declare var obj: Obj;
let key = 'a';
let value = getObjectValue(obj, key);
value.toFixed();
value.replace('', ''); // Error !!
これは恐らくバグだとは思うのですが、v0.50.0では引数にこの型を利用するとempty
を返します。(empty
はflow.jsのdumpTypes
で出力される推論できなかったときに発生する型)
declare function getObjectValueFunction<O: Object, K>(obj: O, key: K): (v: $ElementType<O, K>) => void;
type Obj = {
a: number;
b: string;
c: boolean;
};
declare var obj: Obj;
let key = 'a';
let fn = getObjectValueFunction(obj, key);
fn(1);
fn('b'); // Not Error !!
fn(true); // Not Error !!
fn([]); // Not Error !!
fn(() => {}); // Not Error !!
fn(null); // Not Error !!
fn(void 0); // Not Error !!
そのため、例えばObject
に値をセットするような関数などでは型で縛ることはできません。
とりあえずは値を取得するときに利用するしかないと思いますが、型の表現の幅はかなり広がるのでおもしろいUtility Typeです。
$Values
$Values
はT
に指定したObject
の値のUnion Typeを返します。
ひらたく言うと$Keysの値を返す版ですね。
type Obj = {
a: number;
b: string;
c: boolean;
};
declare var obj: Obj;
declare var value1: $Values<Obj>;
let value2: number | string | boolean = value1;
let value3: number | string = value1; // Error !!
使い所としてはObject.valuesぐらいだとは思います。
$Rest
$Rest
はObject Rest Propertiesを表現する型です。
let t1 = {a: 1, b: 'foo', t2: true};
let {t2, ...rest} = t1;
というようなコードにおけるrest
の型を表現する型になります。
type Obj = {| a: number, b: string, c: boolean |};
declare var o: $Rest<Obj, {| c: boolean |}>;
(o: {| |}); // Error: property not found `a`, `b`
(o: {| a: number, b: string |});
基本的に$Rest
は上記のようにExact Typeを使うと想定通りの動きします。
しかし、T2
にObject
を指定すると
type Obj = {| a: number, b: string, c: boolean |};
declare var o1: $Rest<Obj, {c: boolean}>;
(o1: {| |}); // Error: property not found `a`, `b`, `c`
(o1: {| a: number, b: string, c: boolean |}); // Error: undefined. This type is incompatible with `number`, `string`, 'boolean'
(o1: {| a?: number, b?: string, c?: boolean |});
declare var o2: $Rest<Obj, {}>;
(o2: {| |}); // Error: property not found `a`, `b`, `c`
(o2: {| a: number, b: string, c: boolean |}); // Error: undefined. This type is incompatible with `number`, `string`, 'boolean'
(o2: {| a?: number, b?: string, c?: boolean |});
というようにT2
の型に関わらず、全てのプロパティーがOptionalになります。
(一応、ドキュメントにはこの理由も記載されているのですが、ちゃんと理解できていません。Object
型は全てのObject
の部分型であるため、T1
のすべてのプロパティがT2
に存在する可能性があり$Rest
の返す型のプロパティはすべてOptionalになる?でも、それならo1
のように指定した場合はプロパティc
が存在することは確定するので、プロパティc
はo1
から除外されるべきだし・・・と納得ができていません)
また、同じような挙動をする$Diffでは、$Rest
と違いT2
がObject
でもExact Typeでも同じように動作します。
$Call
$Call
は関数の戻り値型を返す型です。
type Fn1 = () => number;
declare var call1: $Call<Fn1>;
call1.toFixed();
call1.replace('', ''); // Error
type Fn2 = <A, B>(a: A, b: B) => B;
declare var call2: $Call<Fn2, number, string>;
call2.toFixed(); // Error
call2.replace('', '');
上記のようにFn
に関数の型を指定し、続く型に引数の型を渡すことで戻り値の型を取得することができます。
$Call
の説明として足りていなかったので別記事にて詳細を記載しました。
$CharSet
$CharSet
はT
に指定した文字集合を表した型になります。
つまり、
("a": $CharSet<"ab">);
("b": $CharSet<"ab">);
("ab": $CharSet<"ab">);
("ba": $CharSet<"ab">);
("aaaa": $CharSet<"ab">); // Error
("c": $CharSet<"ab">); // Error
("ac": $CharSet<"ab">); // Error
上記のようにT
で指定した文字が存在する文字列(存在しない文字は含まず、重複も許容しない)型になります。
文字を利用したフラグ管理などを使っていれば利用できそうではありますが、コマンドライン引数のフラグのように外部から入力された値には使えないため、かなり利用用途の低そうな型です。
$Compose、$ComposeReverse
$Compose
はHoC(Higher-order Components)を実現する関数の型になります。
つまり、関数を合成する関数の型で、$Compose
は引数の後ろから前に呼び出されていく関数になり、$ComposeReverse
はその逆になります。
declare var compose: $Compose;
compose(
s => s.replace('', ''),
n => n.toString()
)(42);
しかし、上記のコードを実行するとcompose
関数の定義がない状態になるため実行時エラーが発生します。
最近のReact事情はさっぱりなのですが、これは一体どこで使えばよいのでしょうね。
$ReadOnly
$ReadOnly
はT
に指定したObject
を読み込みのみに可能なObject
を表す型です。
$ReadOnlyArrayが出た時にゴリ押しでReadOnlyなObject
型を定義していましたが、これよりスマートに記述することができるようになりました。
type Obj = {a: number, b: string, c: boolean};
declare var obj: $ReadOnly<Obj>;
obj.a = 1; // Error
$ReadOnlyArray
と同様に書き込みがされないことを担保できるようになるので、あちらこちらで使うことができそうですね。
おわりに
という訳でUtility Typeのまとめ その3でした。
ここが間違っている、例示が悪いなどあれば、ぜひご指摘ください。