flowtypeのUtility Typeについて その2

flowtypeの[v0.38.0](https://github.com/facebook/flow/releases/tag/v0.38.0)がでましたね！
CHANGELOGを読んでみると

> $ReadOnlyArray<T> is the common supertype of arrays and tuples. It's basically an array without stuff like push(), pop(), etc

`$ReadOnlyArray<T>`なんて便利そうなUtility Type（旧Magic Type）が追加されてるじゃないですか！！
これは楽しそうだと

* https://github.com/facebook/flow/blob/master/src/typing/type_annotation.ml

を見てみるとなんか`$`から始まる見知らぬ型が増えてる・・・。
ドキュメントにないのはまだ許せるけど、CHANGELOGにすらないってどういうことなの・・・！！！

嘆いても仕方ないので、気合を新たに調べ直してみました。


ちなみに、その他のUtility Typeについては下記を参考にしてください。

* [flowtypeのUtility Typeについて その1](http://qiita.com/kinzal/items/e1898c89af5618e18334)
* [flowtypeのUtility Typeについて その3](http://qiita.com/kinzal/items/8e2978be3ec78663c70b)もできました。

また、Utility Typeの調べ方がわかったので`$Type`や`$Abstract`などよくわからなかった型についてもその1にて更新してあります。

## Utility Typeの調べ方

flowtypeで見知らぬ型や構文を見つけたときに皆さんはどこを調べていますか？
自分は最初は[ドキュメント](https://flowtype.org/docs/getting-started.html#_)や[issue](https://github.com/facebook/flow/issues)、[PR](https://github.com/facebook/flow/pulls)、果ては[stackoverflow](http://stackoverflow.com/questions/tagged/flowtype)なんかを調べていました。
しかし、ドキュメントは更新遅れてるし、issueやPRなんかは量が多くて埋もれるし、Utility Typeの名前によってはノイジーな情報に埋め尽くされて探せたものじゃありませんでした。

そういったときは

* https://github.com/facebook/flow/tree/master/tests

の`tests`以下を見ると実際にJSでUtility Typeを利用しているコードが存在するので、そちらを見ると利用イメージが湧きやすく、どんな型なのか分かると思います。
（`$Abstract`とかはなくて泣きそうになったけど）

全くもって初心者に優しくないですが、これなに？となったときはテストを読めの精神ですね！！！


# $ReadOnlyArray<T>

`$ReadOnlyArray`は`T`型の読み込みのみ可能な配列を表す型です。
そのため、要素への書き込みや、`push`や`pop`などの配列を変更する処理が行えなくなります。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVA3AhgJzLnALjABIAlAUywBMB5AOxgE8BBHHLJgHnoFcBbAEYUcAPjABeMAG0AjABowAJkUBmALoBuVAWkAGdZLAAWTWGDAwAUXZw8AQh3sAdAAdeAZwAWACmMBKMwtrWwcnHDc4Vx9A80sbHDsweyA)

```js
var arr: $ReadOnlyArray<number> = [1, 2, 3];
arr[0] = 4; // Error !
arr.push(4); // Error !
arr.pop(); // Error !
```

変更が必要ないところは多いので、これは便利ですね。
余談ですが、`Object`でもこういった機能が欲しいなと思ったときにsetter/getterを利用することでReadOnlyObjectを作ることができます。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgBKeAhgCYDyAdjFhQEYBWeAxhmALxgDeqYYAczzsSACgCUALjBUArgFt6eAE4BuPoOFh6E6QGcMygJZUB6-kPYtd2uHBikq6gL7rUANxLKwcJtOLk1LQMzGycPCTSAIwANNrSAOQk9CwJcSzShrJ4rqi+jAB0JAUYcABiRgAeeGQS6vlFBcp4ODAkLHiiCWlg3eKqYMDAYACiyspw3gCEeUxF4QBMA0Oj45NgU0A)

```js
type ReadOnlyObject = {
  get a(): number;
  get b(): string;
  get c(): boolean;
};

var obj: ReadOnlyObject = {a: 1, b: 'abc', c: true};
obj.a.toFixed();
obj.a.replace('', ''); // Error !
obj.a = 2; // Error !
```

とはいえ、ちょっとイケてない感はあるので、flowtypeにてReadOnlyObjectを対応して欲しいものですね。

# $TupleMap<T, F>

`$TupleMap`は`T`に指定したTupleの値を`F`に指定した関数の型の通り変換する型です。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVATApgYxgQwCcswA3IsAfQC4wBtAQUMPwE8AeAOwFcBbAIyyEAfABowTFhwDOAF0IBLTgHMxE5m3b84cGFnydhAXQDc6WawAOJBmAC8YACQAVbpb0BZfJfYXrcKAAKSgBKcXYAaWFAgGtqSU0okPthMCizMEys7MzMXAJiMgp8WgYzfDoABiMAOlk4ADEFAA8sDECQ8qra4nd8HCxAgHIh8RHOsGBgMABRZjhCMABCJdQKgEZa+qbW9omp2fnFlbW6TZreggHh0bBxroAmLcaWto6TSem5wgXl1YqnhcsH1riMxkN9l8jn8gA)

```js
declare var _: [Array<number>, Array<string>, Array<boolean>];

type A = $TupleMap<typeof(_), <K>(k:Array<K>) => K>;
            
declare var a: A;
a[0].toFixed();
a[0].replace('', ''); // Error !!
a[1].toFixed(); // Error !!
a[1].replace('', '');
a[2].toFixed(); // Error !!
a[2].replace('', ''); // Error !!
```

上記、サンプルでは各要素の`Array<*>`な型を剥いて内部の型に変換しています。
このように一括で型を剥いたり、被せたりもしますが[Intersection Type](http://qiita.com/kinzal/items/e1898c89af5618e18334#allt)を利用することで、個別に一致した型を変換するということもできます。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVATApgYxgQwCcswA3IsAfQC4wBtAOwFcBbAIy0IBowBnAF0IBLBgHMebOHBhZ8DALoBudPwCeABxIBBMAF4wAEgAqTdTICy+dQB41muFAAUlAJQ9HjgNbVm7Ti90APgFhMRcAMg9vEJFRAMDJaVkGF0DlMAzMrIzMXAJiMgp8Wi1lfDoABnkAOn44ADEhAA8sDEcXRTBgYDAAUUJCOEIwAEIR1HKq6uIzfBwsRwByRZ5ljom6AEYausaWto6unv7B4bGN7emsWfmllbA1zu6+gaHR8fKAJh2G5tb2p7HV5nD50b5XG4LZarRaHZ4nN5jIA)

```js
declare var _: [number, string, boolean];

type A = $TupleMap<typeof(_), ((k:number)=>string)&((k:string)=>boolean)>;
            
declare var a: A;
a[0].toFixed(); // Error !!
a[0].replace('', '');
a[1].toFixed(); // Error !!
a[1].replace('', ''); // Error !!
a[2].toFixed(); // Error !!
a[2].replace('', ''); // Error !!
```

このとき、`a[2]`では対応する型変換の関数がIntersection Typeで定義されていないためエラーとなっています。
そのため、`$TupleMap`を使うときは全ての要素を処理できる関数を型変換の関数として定義してあげる必要があります。

これは、後述の`$ObjMap`、`$ObjMapi`でも同様の挙動になります。

# $ObjMap<T, F>

`$ObjeMap`は`T`に指定したObjectの値を`F`に指定した関数の型の通り変換する型です。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVATApgYxgQwCcswA3IsAfQC4wBvfWgQUMPwE8AeAOwFcBbAEZZCAPgA0YQc1YdOAZwAuhAJbcA5hLA4ZbLoLhwYWfN1EBfANzpF7AA4kmYALxgAJAHlBAKwCy+O05bBzgoAApKAEpJTgBpUTCAa2oWPTjRSJdRMHjrMHyCwvzMXAJiMgpGMCZrfAA6esU4ADEVAA8sDDDI2oa64jsCHCwwgHJRyXGesGBgMABRVjhCMABCVHrBOqbWjq7p2YWllfXN-qxB-GGxibAp3pxtlvbO7ssZucXCZbWNuseBkMRuNJqMDp9jmsgA)

```js
declare var _: {a: Array<number>, b: Array<string>, c: Array<boolean>};

type A = $ObjMap<typeof(_), <K>(k:Array<K>) => K>;
            
declare var a: A;
a.a.toFixed();
a.a.replace('', ''); // Error !
a.b.toFixed(); // Error !
a.b.replace('', '');
a.c.toFixed(); // Error !
a.c.replace('', ''); // Error !
```

`$TupleMap`のObject版という形で同様の書き方で変換を行います。

# $ObjMapi<T, F>

`$ObjMapi`は`T`に指定したObjectのキーに一致した値を`F`に指定した関数の型の通り変換する型です。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVATApgYxgQwCcswA3IsAfQC4wBvAMQHknaA7AVwFsAjLQgDRgAQgEEASu259CAXwDc6AC4BPAA4lRYALxgAJEx4ArALL41ASwA8qjXCgAKSgEohDqwGkAfA4DW1AHJmJgDnHS8wAGclQgs2AHNnADJ3bz9AsXFQ8LBRQkJ8FSsQL2cvRTBKqurKzFwCYjIKfFpRRXwAOmCOpTgGCwAPLAwHZ3kwYGAwAFF8uEIwAEJUTu7iNQIcLAcAgKFdsZWOzJ6+weHR8cmZuYXlzpOjODid0PkgA)

```js
declare var _: {FOO: number, BAR: number};

type A = $ObjMapi<typeof(_), (<K>(k:'FOO') => string)&(<K>(k:'BAR') => Array<*>)>;
            
declare var a: A;
a.FOO.toFixed(); // Error !
a.FOO.replace('', '');
a.BAR.toFixed(); // Error !
a.BAR.join('');
```

上記のように`FOO`に一致したキーの型をstringに、`BAR`に一致したキーの型をArray<*>に変換しています。
`$ObjMapi`も`$TupleType`や`$ObjMap`と同様にIntersection Typeを使わずに全てのキーに一致する（つまりstringに一致するキー）値を変換するということができます。

# $Tainted<T>

前回、書き忘れていたUtility Typeです。
`$Tainted`は`T`に取った型を受け取れる代わりに、利用をできなくするための型です。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVA3AhgJzAFwEYAuMAEgBUsBLAO3wFMATAHloFcBbAIwZwD4wAXjCEA3JlwEATKSy0AnsILiwwYGACiOHHDwBCSXnwBmUp2oAPZsqJi1G7boNGCAFlKUa9Zmy68BW3F0AGM4WgBnOBgGADp4AHMACiIASnt1LR09MEMiWPw4ADErZiT0hyznXKA)

```js
var t1: $Tainted<number> = 1;
var t2: any = t1; // Error !
var t3: mixed = t1; // Error !
var t4: $Tainted<number> = t1;

console.log(t1); // Error !
t1.toFixed(); // Error !
```

上記のように、何でも取れるはずの`any`や`mixed`に変換することができなくなり、受け取った`T`の型としても利用ができなくなります。

# $Pred<NumberLiteral>

前回、書き忘れていたUtility Typeその2です。
`$Pred`は述語を取る関数で`NumberLiteral`に引数の数を指定します。
述語を取る関数というは

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVATApgYxgQwCcswoBXAOxwBcBLOCsWgZwGVrDaKBzARgAoAHgC4wAW1qCsGAJSiARnDgws+RgFIcAC1wBrZv2oBPAA5Y4UMILABeO2ADkzDl24OZAblTkqdBkzYXHgAmIVEJKVkFJRU1ME0dHH0wAG9UMDBiajJCRmMzCytbeycgty8AX3RsPCISADciMHxwyWkvWkt+FnZOHgF8GRlU9OaAOmITAhwsfgcHABpHd0rUTrBuwL7uUMHhtIz8Cawp-Bm5xeXPVAqgA)

```js
declare function isString1(x: mixed): boolean %checks(typeof x === 'string');
function isString2(x: mixed): boolean %checks {
  return typeof x === 'string';
}

declare var a: mixed;
if (isString1(a)) {
  a.replace('', '');
}
if (isString2(a)) {
  a.replace('', '');
}
```

のように戻り値に`%checks`という構文を取り、関数で行ったDynamic Type Testingの結果を呼び出し元のスコープに反映する関数です。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVATApgYxgQwCcswoBXAOxwBcBLOCsWgZwGVrDaKBzARgAoAHgC4wAW1qCsGAJSiARnDgws+RgFIcAC1wBrZv2oBPAA5Y4UMILABeO2ADkzDl24OZAblTkqdBkzYXHgAmIVEJKVkFJRU1ME0dHH0wAG9UMDBiajJCRmMzCytbeycgty8AX3QANyJSCl5RABIABWIMAB5eAD5bAPZOHl4vWsJ64Oa26S7em36y4JG6qAoAZkn2juDZ+cHuRbBgYDAAUUJCODGAQlRR+oAWDentvv58UQoyMXksQgAaMDyD5fH6EGS2Xr4MAAakBHkOxzOF2uQA)

```js
declare function isString1(x: mixed): boolean %checks(typeof x === 'string');
function isString2(x: mixed): boolean %checks {
  return typeof x === 'string';
}

var fn1: $Pred<1> = isString1;
var fn2: $Pred<1> = isString2;
var fn3: $Pred<2> = isString2; // Error !
var fn4: $Pred<2> = (a: number, b: number) => a + b; // Error !
```

`$Pred`はそういった述語のある関数を取るための専用の型になります。

# $Refine<T, P, NumberLiteral>

前回、書き忘れていたUtility Typeその3です。
`$Refine`は`T`にベースとなる型を取り、それを変換する`$Pred`の関数を`P`に、`NumberLiteral`に`T`の数（※ ここの理解がちょっと怪しい）を取って型を変換する（洗練させる）型になります。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVATApgYxgQwCcswoBXAOxwBcBLOCsYqWirAHgBUAaMABQBcYACR9iGdgEYAfNIAUANyE8wOAEZC+ASiHCASlhZsu3PtxkBuTLgLFSlGvUa0AzgGVqhVgHM5ADyEAW1o-LAwdMDU4OBgsfEYAUhwAC1wAaxc5agBPAAcsOCgwPzAAXnKwACIXTx9KrStrPCISBSIwfCCQsKs2wkiypkNWLDl8XlcPLwpvBtQ1ADpiXIIcUYBydd5NrSA)

```JS
declare function refine<T, P: $Pred<1>>(v: T, cb: P): $Refine<T,P,1>;
declare function isString(x: mixed): boolean %checks(typeof x === "string");

declare var a: mixed;
var b = refine(a, isString);
b.replace('', '')
```

ちなみに、`$Refine`は強制的に型を変換するため、

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVATApgYxgQwCcswoBXAOxwBcBLOCsYqWirAHgBUAaMABQBcYACR9iGdgEYAfNIAUANyE8wOAEZC+ASiHCASlhZsu3PtxkBuTLgLFSlGvUa0AzgGVqhVgHM5ADyEAW1o-LAwdMDU4OBgsfEYAUhwAC1wAaxc5agBPAAcsOCgwPzAAXnKwACIXTx9KrStUBSIwfCCQsLKwSStmwkiu5lYsOXxeVw8vCm8G1DUAOmJcghwRgHI13g2tOfnqOAAxDow5BrBgYDAAUUJCOH6AQiA)

```js
declare function refine<T, P: $Pred<1>>(v: T, cb: P): $Refine<T,P,1>;
declare function isString(x: mixed): boolean %checks(typeof x === "string");

var a: mixed = 1;
var b = refine(a, isString);
b.replace('', '')
b.toFixed(); // Error !
```

のように変数`a`にstring以外の型を入れても、変数`b`ではstringとして扱われます。
何かに使えそうな感じはあるのですが。どういったときに使えば良いのかなかなか悩ましいです。

# おわりに

いかがでしたでしょうか。これでv0.38.0時点で存在する`$`から始まる特殊な型は網羅できたと思います。
ものによっては説明が間違っている可能性もあるので、ここの理解がおかしいよなどあればお気軽にツッコミください。