flowtypeのUtility Typeについて その3

この記事ではflowtypeの[v0.50.0](https://github.com/facebook/flow/releases/tag/v0.50.0)から[v0.59.0](https://github.com/facebook/flow/releases/tag/v0.59.0)にかけて追加されたUtility Typeについて記載しています。
それ以前のバージョンで追加されたUtility Typeについては下記を参照してください。

* [flowtypeのUtility Typeについて その1](http://qiita.com/kinzal/items/e1898c89af5618e18334)
* [flowtypeのUtility Typeについて その2](http://qiita.com/kinzal/items/c55f81e6af1a22a4763f)

# $ElementType<T, string>

https://flow.org/en/docs/types/utilities/#toc-elementtype

`$ElementType`は`T`に`Object`を指定して、`string`にそのキーの文字列を渡すと対応した値の型を返します。
これだけ聞くと[$PropertyType](http://qiita.com/kinzal/items/e1898c89af5618e18334#propertytype)と同じように感じますが、`$PropertyType`ではキーに`String Literal`のみの指定だったのが、`$ElementType`ではキーに`String`を指定できるため適用できる範囲が広くなりました。

[# Try Flow](https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVATApgYxgQwCcswoBXAOxwBcBLOCsAcy2oHkAjAK12oDV8MMlgA8bAFxhOPGgBowAaQB8ACjjdJbeQGssAT0kKAlJIAkAURhYAtlgrUAKnoAOorYqUBudNRclpYAC8YADeqGBg+JIUZNYcWITeERySAM7UhLQUTElgOJIccHBW+BTeAL7e2HhEJABuRGDqXJrc3lbUYLp6QWAA5Ph97axgDUIkwSzs3LwC42rcOvpG3mPCAHTUcABitAAeWBgqK6hrWOvEzgQ4WCp9ffL3K2DAwGDmhIRwhGAAhL9AA)

```js
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`で出力される推論できなかったときに発生する型）

[# Try Flow](https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVATApgYxgQwCcswoBXAOxwBcBLOCsAcy2oHkAjAK12oDV8MMlgBilGvQoAeNgC4wnHjQA0YANIA+ABRxu8tqoDWWAJ7y1ASnlaAbvIAkAURhYAtlgrUAKiYAOWGVVNCzAAXg0wGzhaDABudGo-EkUwsABvVDAwfHkKMlcOLEJ4rI55AGdqQloKJhKwHHkOODgXfAp4gF947DwiEhsiMF0ufW54l2owYxNUgHJ8OYnWUkZQ5lZFXgEhUXE6Bh1uI1MLeKgKLVtBYRDw9MzIm6wAOmo4EVoADywMLTPHoNdi9iL4CDgsFo5nNVNCzmBgMAwAA5OBTRyEQhwQhgACEuNQnTOQA)

```js
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<T>

https://flow.org/en/docs/types/utilities/#toc-values

`$Values`は`T`に指定した`Object`の値の[Union Type](http://qiita.com/kinzal/items/e1898c89af5618e18334#either)を返します。
ひらたく言うと[$Keys](http://qiita.com/kinzal/items/e1898c89af5618e18334#keys--enum)の値を返す版ですね。

[# Flow Type](https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgDyARgFZgC8YA3qmGAIYBcYAdgK4C2xeATgNx0wxFgGcMvAJasA5oPoBjFsThwYeBq0EBfQQBM8CmA14EAbibBwyLEqX2HjpsBd4uGMdngCMLACQAah5eogA8dgB8gqjqGO6eeABMLBzcfGAAPmDiUrKZwqrqmpTxXt6CsaV4AMwpXDxuWTnSMiUWCeVgwMBgAKK8vHBuAITDQA)

```js
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](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/values)ぐらいだとは思います。

# $Rest<T1, T2>

https://flow.org/en/docs/types/utilities/#toc-rest

`$Rest`はObject Rest Propertiesを表現する型です。

```js
let t1 = {a: 1, b: 'foo', t2: true};
let {t2, ...rest} = t1;
```

というようなコードにおける`rest`の型を表現する型になります。

[# Flow Type](https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgDyARgFZgC8YA3gD5gCGAXGAHYCuAtsXgE4A0YYiwDOGXgEtWAc0EBjFsThwYeBqzC0AvgG50AEzxyYDXgQBupsHBYASAEp4xAHhKlBdMAqHLV6zVoAfHoAFDY09NoAlDpgwMBgAKK8vHC8LDip+LzYbHAYYFBw7Kz6YAAGDOWC5cTlqGEsnsxsXDwCQqLiUtIBMUA)

```js
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](https://flow.org/en/docs/types/objects/#toc-exact-object-types)を使うと想定通りの動きします。
しかし、`T2`に`Object`を指定すると

[# Flow Type](https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgDyARgFZgC8YA3gD5gCGAXGAHYCuAtsXgE4A0YYiwDOGXgEtWAc0EBjFsThwYeBqzC0AvgG50AEzxyYDXgQBupsHACMLACQAlPGIA8JUoOoKhy1eq0APj0AClsWOk0tAEodMGBgMABRXl44XhYcNPxebDY4DDAoOHZWfTAAAwYKwQriGsq5CtQwuxp6ZjYuHgEhUXEpWTAfJRU1DW1Y+MSUtIywUsMoKTx9ADowABUACwkRMGx8MD3j1jk4ThwGDAliVTAECQxtyo5uPgaKsUkZBoByUb+Vh-FrhdqMAD8LDePUExChYG+g3kCMB4yisQMRhMZjAll41gATA5nG4PF4gqE4MTwZM4glkql0plsnw8qwCkUSmVKtVavVak1QTTIp0YXw4f0fkMRn50XTpoy5ixFnhlqxVhsdidDgQTlJzpdrrd7o9nq9uh9akjfoIAXL1CCwiKOgjxb14VLkcNUQ6JjEdEA)

```js
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](https://flow.org/en/docs/types/utilities/#toc-diff)では、`$Rest`と違い`T2`が`Object`でもExact Typeでも同じように動作します。

# $Call<Fn, T1, ...T2>

`$Call`は関数の戻り値型を返す型です。

[# Try Flow](https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgBiAdgIxgC8YAFAJSUB8YxArgLYBGeATgNyoATPAGMYAQ24EAbhLDCxMGKQBcYACQBhBTAA8JUg37zFpAHQY4hAJYAPPALpHtZyTnHC81AOReANGB9aXjBgYDAAUW5uOG50bHwiYgAmSjAdAEF-ACEGajFVTLAOVSz6CiYs-iFRCWlZYxgk1U1tPWT-Vk4efwBnDG4rYgBzQ1QGpPNLW3tHELDI6Njx01d3Tx9-QN4gA)

```js
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`に関数の型を指定し、続く型に引数の型を渡すことで戻り値の型を取得することができます。

* [flowtypeの$CallというHigher Kinded Type](https://qiita.com/kinzal/items/0a37c31e9c17dd7b93a8)

`$Call`の説明として足りていなかったので別記事にて詳細を記載しました。

# $CharSet<T>

`$CharSet`は`T`に指定した文字集合を表した型になります。
つまり、

[# Try Flow](https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAKARAQywLjABIBhACxwCcBlAUwBcAeXAIywD4BKAbkyzcKkKNBsxxsuvbOILFyVOk1YcefFnkHyRSmZL44DGucMViJPMMGBgAopUpxKfAMayhC0csmXrdh0+lXTRNPXQsrW3tHVCA)

```js
("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`はその逆になります。

[# Try Flow](https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVATApgYxgQwCcswA3IsHOAWwAc4BnLALjABIBhG+pgbnSp1GWABSowYBmAC8APkkA6YrQI5RAcnUAaMJoCUW8WAB2M+cYUAXOAGVLhAJbGA5iL2o9IgCwAmd0A)

```js
declare var compose: $Compose;

compose(
  s => s.replace('', ''),
  n => n.toString()
)(42);
```

しかし、上記のコードを実行すると`compose`関数の定義がない状態になるため実行時エラーが発生します。
最近のReact事情はさっぱりなのですが、これは一体どこで使えばよいのでしょうね。

# $ReadOnly<T>

https://flow.org/en/docs/types/utilities/#toc-read-only-obj

`$ReadOnly`は`T`に指定した`Object`を読み込みのみに可能な`Object`を表す型です。
[$ReadOnlyArray](https://qiita.com/kinzal/items/c55f81e6af1a22a4763f#readonlyarray)が出た時にゴリ押しでReadOnlyな`Object`型を定義していましたが、これよりスマートに記述することができるようになりました。

[# Try Flow](https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgDyARgFZgC8YA3gIYBcYAdgK4C2xeATgDRjGMAzhi4BLJgHM+AY0bE4cGHlpMAvgG50AEzzSYtLgQBuBsHDKMAJACVlWwkxhYAPCVIA+TedIA6WpTAARnUwYGAwAFEuLjguVCA)

```js
type Obj = {a: number, b: string, c: boolean};

declare var obj: $ReadOnly<Obj>;
obj.a = 1; // Error
```

`$ReadOnlyArray`と同様に書き込みがされないことを担保できるようになるので、あちらこちらで使うことができそうですね。

# おわりに

という訳でUtility Typeのまとめ その3でした。
ここが間違っている、例示が悪いなどあれば、ぜひご指摘ください。