flowtypeのUtility Typeについて その1

https://github.com/facebook/flow/releases/tag/v0.31.0

flowtype v0.31.0がでましたね！（既にv0.32.0もでてますけど）
CHANGELOG読んでみると

> Added a new "magic" type called $PropertyType<T, 'x'>. This utility extracts the type of the property 'x' off of the type T.

magic typeなる気になる用語があるじゃありませんか！！
どうやら`$`から始まる定義済みの型のことを指してるようなのですが、公式のドキュメントや過去のCHANGELOGを見てもそんなものは一切出てきません。
仕方ないので実際にコードを見てみると

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

`type_annotation.ml`の中に`$`から始まる型があるので、名前とコメントから推測しつつ、[flowtype.orgのtry](https://flowtype.org/try/)で確認しながらどういった型なのか調べてみました。

ちなみにですが、flowtypeの[ドキュメント](https://flowtype.org/docs/utility-types.html)的にはUtility Typeというが正しいようです。（2017/01/07追記）

# $Either<...T>

`$Either`は`|`と等価で指定した型のいずれかを表す型（[Union type](https://flowtype.org/docs/union-intersection-types.html#union-example)）になります。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgCCYAvGAN4CGAXGAHYCuAtgEZ4BOANGC7QM4Z2ASzoBzAL4BuTLgIAhUhV5gBwsdwDGtRqw5TUAN0rswNMABIAokIwALDgB5C3OQD5FVWgEZuygOS2cKJ4fvpGJspWNvbsTi7uZOT+UAyilH6atABMYcZgWhbWdo7OYG4eZgDMvrR+KWkZ+bQALLkmACa0UcWxpeWJZgCsmWAAbFJgwMBgluzscCYAhItAA)

```js
type A = {a: number, b: string};
type B = {b: string, c: number};
var a: $Either<A, B> = {a: 1, b: 'hoge'};
var b: $Either<A, B> = {b: 'fuga', c: 2};
var c: $Either<A, B> = {a: 3, b: 'fuga', c: 4};
var d: $Either<A, B> = {a: 5, c: 6}; // Error !!
```

この例のように`$Either`を利用すると`A`または`B`と一致する構造の`Object`を取ることができます。
flowtypeの仕様として`Object`は部分型になっており、同一のプロパティがあればその型として扱うことができます。
そのため、変数`c`では`A`と`B`に一致しているのためエラーにはなりません。

厳密に型チェックしたいときは後述のExact typeを使用してください。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgCCYAvGAN7b4BcYA5ABZwDmedANGAHYCuAtrV58ARngBOAXwDcmXAQBCpClTy06UHswCGHMAGcMY2gbEBLLs2noAblrFgttACQBRUxgbiAPIU7yAfEqUcmpMrLpCtACMVqhaAHQqMglCUmDAwGAuYmJw9gCE+XHxJmkZWTl5YIXoplBgABQJKqQkZIwsbACUFKhgDvGpfQOl6ZnZuQVFEqh1jc1yre0a2nQ95MMp-GXjlVObJYYyEkA)

```js
/* @flow */

type A = {type: 'hoge', num: number};
type B = {type: 'fuga', str: string};
var a: $Either<A, B> = {type: 'hoge', num: 1};

a.type;
a.num; // Error !!
a.str; // Error !!

if (a.type === 'hoge') {
  a.num;
  a.str; // Error !!
}
if (a.type === 'fuga') {
  a.num; // Error !!
  a.str;
}
```

また、実際に利用する場合は型を判別する必要があります。
判別前の場合では変数`a`に入っている値が`A`なのか、`B`なのか判別できないため、共通しているキー以外はエラーが発生します。
そこで変数`a`が`A`か`B`どちらかの型であることを`if`を使い[Dynamic Type Test](https://flowtype.org/docs/dynamic-type-tests.html)で判別することで、`type`が`hoge`の`A`ならプロパティ`num`が利用できるようになり、`type`が`fuga`の`B`ならプロパティ`str`が利用できるようになります。

# $All<...T>

`$All`は`&`と等価で指定した型の全てと一致する型（[Intersection Type](https://flowtype.org/docs/union-intersection-types.html#intersection-example)）になります。

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

```js
type A = {a: number, b: string};
type B = {b: string, c: number};
var a: $All<A, B> = {a: 1, b: 'hoge'}; // Error !!
var b: $All<A, B> = {b: 'fuga', c: 2}; // Error !!
var c: $All<A, B> = {a: 3, b: 'piyo', c: 4};
var d: $All<A, B> = {a: 5, c: 6}; // Error !!
```

この例のように`$All`を利用すると`A`と`B`の両方に一致する構造の`Object`を取ることができます。
`$Either`とは違い型の判別は必要ないため、`a`、`b`、`c`のキーはそのまま利用することができます。

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

```js
type A = {a: number, b: string};
type B = {b: boolean, c: number};
var a: $All<A, B> = {a: 1, b: 'hoge', c: 2};
var b: $All<A, B> = {a: 1, b: true, c: 2}; // Error !!
var c: $All<B, A> = {a: 1, b: true, c: 2};
```

また、同名のキーがある場合は先に指定したキーの型が優先されます。

# $Tuple<...T>

~~`$Tuple`は`[...T]`と等価で要素毎に違う型を指定できる型です。（`Array<T>`の場合は要素全てが`T`である必要がある）~~

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

~~```js
var a: [number, string, boolean] = [1, 'hoge', true];
var b: [number, string, boolean] = [1, 2, true]; // Error
var c: [number, string, boolean] = [1, 'hoge', 2]; // Error
```~~

~~基本的に`Tuple`のような構造を利用するときは名前をつけてオブジェクトにするので使いどころがわかりません。~~

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

~~```js
var a: [number, string, boolean] = [1, 'hoge', true];

a.map((x: number | string | boolean) => x);
a.map((x: number) => x); // Error !!
a.map((x: string) => x); // Error !!
a.map((x: boolean) => x); // Error !!
```~~

~~`[number, string, boolean]`の`Tuple`を`map`で回すときも引数は`number | string | boolean`の型である必要があります。
やっぱり、いまいち利用所が思いつかないです。~~

~~こういう値を返すライブラリで使えるかもしれませんが、そんなライブラリは投げ捨てた方が幸せになれると思います。~~

`$Tuple`は削除されました。`[A, B]`を使いましょう。

# $Supertype<T>

https://flowtype.org/docs/utility-types.html#supertypet

`$Supertype`は`class Klass<-T> {}`と同様に反変を表す型です。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVBjGBDAzrsACTgHMBTMAbwF8BuTHfMAMQFcTswyAPAFzIB2AEwLFyVOuix4CAaUa4AjAB4AtABUAfFVRgwUAQApuALjDqAlGYBucAJZCJqaqmvYATmGyKz8mSrEybQBeMAEyJD98RUMLejdPbAAmXwUVNg4Qr0V4jy8AZlT-ZQzsLPDItNjcxIAWIujlQKzsfNowYGAwAFF3dzhPAEJBqQUwKNwk5S0dPQNjM0sbe0caZ1c8gCMfcYUp5rBQit2ZJOqNz02Uk-wpgBIAZVYABzJ3XgBPV5L2MqztmpgTaFG6TH6ZQ5hCKgs5xC5A+ow5SPF5vT7fZr-NodLq9fpDQZAA)

```js
class Hoge {};
class Fuga extends Hoge {};

class Klass1<-T> {
  fn(x: T): void {}
}
var a1: Klass1<Hoge> = new Klass1();
var a2: Klass1<Fuga> = a1;
var a3: Klass1<Fuga> = new Klass1();
var a4: Klass1<Hoge> = a3; // Error !!

class Klass2<T> {
  fn(x: T): void {}
}
var b1: Klass2<Hoge> = new Klass2();
var b2: Klass2<$Supertype<Fuga>> = b1;
var b3: Klass2<Fuga> = new Klass2();
var b4: Klass2<$Supertype<Hoge>> = b3; // Error !!
```

`-T`の場合には型パラメーターに指定できますが、例えば変数の型や、関数の戻り値には指定できません。
その一方で`$Supertype`で型を指定した場合には変数の型などに指定できる代わりに、型パラメーターには指定ができなくなります。

# $Subtype<T>

https://flowtype.org/docs/utility-types.html#subtypet

`$Subtype`は`class Klass<+T> {}`と同様に共変を表す型です。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVBjGBDAzrsACTgHMBTMAbwF8BuTHfMAMQFcTswyAPAFzIB2AEwLFyVOuix4CAaUa4AjAB5ZAGjABqAGoA+KqjBhuALipgA2gGszsgLpntko4bBQBAChthZASkcGRgCQAE5kvKwhAmC8ABYAlrgAdNzWdvRG1KhZAG7YIWDYirYKKri8IfECJBpiZPoAvGACZEjyMooevvR5BdgATCUdyuWV1RpsHI2FirRgwMBgAKIhIXAFAIQbqL2FAMxD+GUVVTUs7NjTLW2lXT35hQAsh0ojJ+NEpPVgTdh79AwZD4FP1VBo9IFjGZKJZvPZHM4wK53F5bP4wNpIaFwpFonFEik0hkwFlcg8AEbFYEyUGjU61L5XVrU-D9O47CmDFm4WnvM4AEgAyqxybwAJ4ABzIykml2mlLmC2Wq3WYC2HIK5IO3N5YzOsqZNxp7N25OeOreeo0QpF4qlyjqunl-yAA)

```js
class Hoge {};
class Fuga extends Hoge {};

class Klass1<K, +V> {
  x: { [k: K]: V};

  fn(k: K): V {
  	return this.x[k];
  }
}
var a1: Klass1<string, Hoge> = new Klass1();
var a2: Klass1<string, Fuga> = a1; // Error !!
var a3: Klass1<string, Fuga> = new Klass1();
var a4: Klass1<string, Hoge> = a3;

class Klass2<K, V> {
  x: { [k: K]: V};

  fn(k: K): V {
  	return this.x[k];
  }
}
var b1: Klass2<string, Hoge> = new Klass2();
var b2: Klass2<string, $Subtype<Fuga>> = b1; // Error !!
var b3: Klass2<string, Fuga> = new Klass2();
var b4: Klass2<string, $Subtype<Hoge>> = b3;
```
`$Subtype`も`$Supertype`と同様に`+T`と`$Subtype<T>`で指定できるところが違います。


# $Type<T>

`$Type`は`T`と一致した型を取る型です。

[# Try FLow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVBjGBDAzrsAQTAG8wBfTHfMAITAFMAPAFwYDsATA4syrPAQDCjVh251SFdADdsAJzDYAjAC4wAEgAqATwAODADy0AfGAC8RANxhgwMAFF58uPICEqOYuwAmddv0jUws6K08FJQBmf10DYzNLIRs7R2dXDy8wACM1MBZAuChJS0Jk+ycXd3DFLL88gqL6S1owzKzo+oNC4rAk6rAMXKFqXHiQ0ttytKrMjDrhwTHm1oiMDoX8Jd6rIA)

```js
class A { }
class B extends A { }
class C extends B { }

var a1: $Type<B> = A; // Error!
var a2: $Type<B> = B;
var a3: $Type<B> = C; // Error!
var b1: typeof B = A; // Error!
var b2: typeof B = B;
var b3: typeof B = C;
var c1: Class<B> = A; // Error!
var c2: Class<B> = B;
var c3: Class<B> = C;
```

似たようなもので`typeof`や`Class<T>`もありますが、それらと違い`$Type`が厳密に一致する型をとります。
そのため、継承関係にある子のクラスを`T`に渡したさいに、`typeof`や`Class<T>`では通りますが、`$Type`ではエラーとなります。

# $PropertyType<T, 'x'>

https://flowtype.org/docs/utility-types.html#propertytypet-x

`$PropertyType`は`T`に指定した型のプロパティ`x`の型を取得する型です。

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

```js
type A = {x: number};

var a: $PropertyType<A, 'x'> = 1;
var a: $PropertyType<A, 'x'> = 'aaa'; // Error !!
```

ここでプロパティ名に指定できるのはString literalのみになるため、String literalのUnion typeなどは指定できません。
そのため、残念ながら後述の`$Keys`と組み合わせて、オブジェクトのプロパティのUnion typeを作ることはできません。

# $NonMaybeType<T>

`$NonMaybeType`は`T`に指定したMaybe Typeを`null`や`undefined`を許可しない元の型に戻す型です。

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

```js
type A = ?string;

var a1: A = 'hoge';
var a2: A = null;
var b1: $NonMaybeType<A> = 'fuga';
var b2: $NonMaybeType<A> = null; // Error !!
```

# $Shape<T>

`$Shape`は`T`に指定した型の部分型であり、`Object`とは違い`T`と一致しないプロパティを許容しない型になります。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgCCYAvGAN4CGAXGAHYCuAtgEZ4BOANGC7QM4Z2ASzoBzbgGNaLOHBh5KdAL4BudADdK7MDTAASAMoALSvgA8hAHykKugIzdeYAORG4ovM8m1BDPKtRNbSdDE3MrGypaOwCgsCl9Y1M8C2sycidXd09YrTAAE1pQ5NTIhN9-NTi8IqTwtIpCsDsAOhiVIA)

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

var a: $Shape<A> = {a: 1, b: 'hoge', c: true};
var b: $Shape<A> = {a: 1};
var c: $Shape<A> = {b: 'hoge'};
var d: $Shape<A> = {c: true};
var e: $Shape<A> = {d: 1.1}; // Error !!
```

ただし、Union type、Intersection type、後述のDiff typeと組み合わせると、それぞれで許容される型が変わります。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgCCYAvGAN4CGAXGAHYCuAtgEZ4BOANGC7QM4Z2ASzoBzAL4BuTLgIAhUhV5gBwsdwDGtFnDgw8lOlPTBgYAKp04dMNnyoAbpXZhKABje0AJAGUAFpT4ADzEAD5gcgB8iuTGTi7uAIze-oF4IWDhUTE0YIlSYKZgAKLs7HAuAISVjs6ubgBMKQHBYRHRZOTKAOR+cKJ43XF17gDMzWkZWR0UWrbsDHgFRaXlVTXx9QAsE62Z7TEAJrSJAHT5koVmqxVg1bUJbgCsu+lt2Z25idw9fQNDlxWZVu9027gAbK8pgdPidNLRBItltdgesHvUAOxQ94zKhwsDHPLnZElVF3DYjNwADmx+w+SlovX6g3h8yRgJRa3J6PcAE5adMYr9md1uISzhcrqSuaCRolPGBfC03nTcV8foy-iywHNEUsOdKQRSEolkorUntBbC8hqwEz-mKTsSDTc0WDEk1zcrofS8TadQiFnhHUTJUCZcbXIlxl7JjicvjhQ6A2zgwSnWHOUaTGYAJJ0DAcPh4DQYITWWyydEsDy0gBkMIowxcNbNSrjYAbvq+zZ4jXrja6mpFvZrMfbey7uN1QdHbh2scng-Fzqlru5mxrL0XKqnCf9ScGc8hO4ye+t3xTernWNPxHPFHV6dDJPXspb1IHvsPoqvs+km5uPyd6doOP4hhKr5ku+PDyl+aqJsOyYzuy1amvB+6XuBz6QS60GRiwHoYRerJ6hBq7htmm7RsRj6IXaWq-ihaYrpmhpoqgRQACJCFAUCVvYmwaLWp5eDxfEhNwUS4r2wltha6RibxUCSe0CF5FBEbosJnoTop4kqYQUmRLiP6aVRdTCeOClBEpElGWpMTMeZbqWfOUJ2YZxm4qxLkbm5256bZBmqdJmG2vaR54VpQluCeQWeaFJnhX+7Jrvh2luLeCUhQ5YUkThFFZq5LjCTSom5d5QpIdqznRRZpVAR5lWOZ02G+fVJU6nBFXKUl6lYTVTGBmllFdRo6G9fZVUFe1GZ+TBE26TZiV5clBXMeRbFvpGE3Wd6q0zXRB5DaRQZbQtNRAA)

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

// Unon type
var a00: $Shape<A | B> = {};
var a01: $Shape<A | B> = {a: 1}; // Error !!
var a02: $Shape<A | B> = {b: 'hoge'};
var a03: $Shape<A | B> = {c: true}; // Error !!
var a04: $Shape<A | B> = {d: 1.1}; // Error !!
var a05: $Shape<A | B> = {a: 1, b: 'hoge'}; // Error !!
var a06: $Shape<A | B> = {a: 1, c: true}; // Error !!
var a07: $Shape<A | B> = {a: 1, d: 1.1}; // Error !!
var a08: $Shape<A | B> = {b: 'hoge', c: true}; // Error !!
var a09: $Shape<A | B> = {b: 'hoge', d: 1.1}; // Error !!
var a10: $Shape<A | B> = {a: 1, b: 'hoge', c: true}; // Error !!
var a11: $Shape<A | B> = {a: 1, b: 'hoge', d: 1.1}; // Error !!
var a12: $Shape<A | B> = {a: 1, c: true, d: 1.1}; // Error !!
var a13: $Shape<A | B> = {a: 1, b: 'hoge', c: true, d: 1.1}; // Error !!

// Intersection type
var b00: $Shape<A & B> = {};
var b01: $Shape<A & B> = {a: 1};
var b02: $Shape<A & B> = {b: 'hoge'};
var b03: $Shape<A & B> = {c: true};
var b04: $Shape<A & B> = {d: 1.1}; // Error !!
var b05: $Shape<A & B> = {a: 1, b: 'hoge'};
var b06: $Shape<A & B> = {a: 1, c: true};
var b07: $Shape<A & B> = {a: 1, d: 1.1}; // Error !!
var b08: $Shape<A & B> = {b: 'hoge', c: true};
var b09: $Shape<A & B> = {b: 'hoge', d: 1.1}; // Error !!
var b10: $Shape<A & B> = {a: 1, b: 'hoge', c: true};
var b11: $Shape<A & B> = {a: 1, b: 'hoge', d: 1.1}; // Error !!
var b12: $Shape<A & B> = {a: 1, c: true, d: 1.1}; // Error !!
var b13: $Shape<A & B> = {a: 1, b: 'hoge', c: true, d: 1.1}; // Error !!

// Diff type
var c00: $Shape<$Diff<A, B>> = {};
var c01: $Shape<$Diff<A, B>> = {a: 1}; // Error !!
var c02: $Shape<$Diff<A, B>> = {b: 'hoge'}; // Error !!
var c03: $Shape<$Diff<A, B>> = {c: true}; // Error !!
var c04: $Shape<$Diff<A, B>> = {d: 1.1}; // Error !!
var c05: $Shape<$Diff<A, B>> = {a: 1, b: 'hoge'}; // Error !!
var c06: $Shape<$Diff<A, B>> = {a: 1, c: true}; // Error !!
var c07: $Shape<$Diff<A, B>> = {a: 1, d: 1.1}; // Error !!
var c08: $Shape<$Diff<A, B>> = {b: 'hoge', c: true}; // Error !!
var c09: $Shape<$Diff<A, B>> = {b: 'hoge', d: 1.1}; // Error !!
var c10: $Shape<$Diff<A, B>> = {a: 1, b: 'hoge', c: true}; // Error !!
var c11: $Shape<$Diff<A, B>> = {a: 1, b: 'hoge', d: 1.1}; // Error !!
var c12: $Shape<$Diff<A, B>> = {a: 1, c: true, d: 1.1}; // Error !!
var c13: $Shape<$Diff<A, B>> = {a: 1, b: 'hoge', c: true, d: 1.1}; // Error !!
```

Union typeの場合は空オブジェクトと`A`と`B`の共通部分の部分型に。
Intersection typeの場合は空オブジェクトと`A`と`B`の部分型に。
Diff typeの場合は空オブジェクトのみを許容する形になります。

正直、Diff typeのパターンはバグだと思います。変数`c01`のパターンは許容されるべきでは・・・。

# $Diff<T,S>

https://flowtype.org/docs/utility-types.html#diffa-b

`$Diff`は`T`から`S`を除外した型を許容する型（Diff type）になります。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgCCYAvGAN4CGAXGAHYCuAtgEZ4BOANGC7QM4Z2ASzoBzAL4BuTLgIAhUhV5gBwsdwDGtFnDgw8lOlPQA3SuzA0wAEgAiQqFAA8hbnIB8iqrQCMxsxZaNvaOLm6eZN5gPtzKAOQAFnCieHH+5mAAJrR2Ds6uYB5eVjFgQYIMeOkWeDkh+eFe8UkpcZq0FVWSYMDAYACi7OxwFgCEo0A)

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

var a: $Diff<A, B> = {a: 1};
var c: $Diff<A, B> = {a: 1, b: 'hoge'};
var d: $Diff<A, B> = {a: 1, c: true};
var e: $Diff<A, B> = {b: 'hoge', c: true}; // Error !!
```

実際にどう使うかは悩ましいですが、`$Diff`があることで追加だけでなく除外できるようになるため、型の表現力が格段に上がります。

# $Keys<T> | $Enum<T>

https://flowtype.org/docs/utility-types.html#keyst

`$Keys`は`T`に指定した型のキーのUnion typeになります。

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

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

var a: $Keys<A> = 'a';
var b: $Keys<A> = 'b';
var c: $Keys<A> = 'c';
var d: $Keys<A> = 'd'; // Error !!
```

この場合、`$Keys<A>`は`'a' | 'b' | 'c'`と等価になります。

# $Exact<T>

`$Exact`は`T`に指定した型の同型のみを許容する型になります。
v0.3.2からは構文が導入され`{| |}`で表現可能になっています。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgCCYAvGAN4CGAXGAHYCuAtgEZ4BOANGC7QM4Z2ASzoBzbgGNaLOHBh5KdAL4BudADdK7MDTAASAKIAPShIwAeQgD5SFXQEZuvMAHIAFnFF4Xk2oIZ4qqia2s6GJmaWNmRUtI48tO6e3qpgwMBgBuzscNoAhHnBWmBS+samFta2sWDxzklePiV+7AHcACZxAHT2qemZ2blgBUA)

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

var a: $Exact<A> = {a: 1, b: 'hoge', c: true};
var b: $Exact<A> = {a: 1, b: 'hoge'}; // Error !!
var c: $Exact<A> = {a: 1, b: 'hoge', c: true, d: 1.1}; // Error !!
```

`$Exact`は`$Shape`と違い、Union type、Intersection type、Diff typeと組み合わせると代入不可能な型になります。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgCCYAvGAN4CGAXGAHYCuAtgEZ4BOANGC7QM4Z2ASzoBzAL4BuTLgIAhUhV5gBwsdwDGtFnDgw8lOlPTBgYAKp04dMNnyoAbpXZhKABje0AJAFEAHpQaGAA8xAA+YHIAfIrkUmCmYD7s7HAuAITpjs6ubgCM3v6BIeGRMWRUtHnxicmpGVlOLu4ATIUBQaFgEdGxygDkABZwonj9NWZ1aWCZ2c1uAMztxV095RRatuwMeBNJKdOzTbkALMudpb0VACZVAHTVkgmTBw1zuQCs5yXdZbE0YDy3AGw1G4yetVeM0aOXcADZvqs-hUAUCwJtBDs9lM3sd3AB2RGXdaVQHcW6Ah7YqFHWFuAAcRN+VyUtCGIzGmlomN2EJe9Wh73cAE4mWs+mzQZywBS8lS+fsBbTmnlPGBfB0fuKUVVgZKOf0uVssQqcYK8XkCuqihdmSTUXqwOyweT7o9norDjCVW1rZqkSzSWiMds8K7Ke7IUrva48ks-Stif9dTx9S70dzQ+G5ZH+V6TGYAJJ0DAcPh4IJCay2WTvFgeJkAMmRFGp0br+SbLaDbfzx3rvo1ibAzcDIINvdxOXr8aHttHJJDJo9ZuVPDcZwT8+7svlK5pMfrXy3PwXybJqadUvB+-b-bcCJPXTPOovS95t7707chKfxBfFAOjKbqTua36Mn+I7duO6bvsY96ipBAHkDB0q7rmnpTi4LCql2gZAahhoZsavJ1paeH2imhHZnuUZftheSDjap7dkB740Rhq6HnGFHnmi1HETyHGgbMqCJAAIkIUBQDW9jHBoDZPl4knSaE3DRCSIkxgpVpziEylSVAallJRgJae8ClMf6BmqYQ6lRCShHmfJiyIjZRl2SZsRwaaB4WRubkqR59kkuhzk5Apx56cE7nGRpfGOs6YzhS4CmPtFsWefFr7Bpmy50Vh6I-oFhlxQ5CXARGKVFRBGVBWVjlptKPmfoVCmIXVpVZeVFQCWFvl3hFuHqgAyoMlD4DF9XdaZ-FNURLUFWBqXkUp00hRVfUgQN9HooxJW2RtOVGkJlU5tVGg8WtXVHYBVHzSdWZnbReYNEAA)

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

// Unon type
var a00: $Exact<A | B> = {}; // Error !!
var a01: $Exact<A | B> = {a: 1}; // Error !!
var a02: $Exact<A | B> = {b: 'hoge'}; // Error !!
var a03: $Exact<A | B> = {c: true}; // Error !!
var a04: $Exact<A | B> = {d: 1.1}; // Error !!
var a05: $Exact<A | B> = {a: 1, b: 'hoge'}; // Error !!
var a06: $Exact<A | B> = {a: 1, c: true}; // Error !!
var a07: $Exact<A | B> = {a: 1, d: 1.1}; // Error !!
var a08: $Exact<A | B> = {b: 'hoge', c: true}; // Error !!
var a09: $Exact<A | B> = {b: 'hoge', d: 1.1}; // Error !!
var a10: $Exact<A | B> = {a: 1, b: 'hoge', c: true}; // Error !!
var a11: $Exact<A | B> = {a: 1, b: 'hoge', d: 1.1}; // Error !!
var a12: $Exact<A | B> = {a: 1, c: true, d: 1.1}; // Error !!
var a13: $Exact<A | B> = {a: 1, b: 'hoge', c: true, d: 1.1}; // Error !!

// Intersection type
var b00: $Exact<A & B> = {}; // Error !!
var b01: $Exact<A & B> = {a: 1}; // Error !!
var b02: $Exact<A & B> = {b: 'hoge'}; // Error !!
var b03: $Exact<A & B> = {c: true}; // Error !!
var b04: $Exact<A & B> = {d: 1.1}; // Error !!
var b05: $Exact<A & B> = {a: 1, b: 'hoge'}; // Error !!
var b06: $Exact<A & B> = {a: 1, c: true}; // Error !!
var b07: $Exact<A & B> = {a: 1, d: 1.1}; // Error !!
var b08: $Exact<A & B> = {b: 'hoge', c: true};
var b09: $Exact<A & B> = {b: 'hoge', d: 1.1}; // Error !!
var b10: $Exact<A & B> = {a: 1, b: 'hoge', c: true};
var b11: $Exact<A & B> = {a: 1, b: 'hoge', d: 1.1}; // Error !!
var b12: $Exact<A & B> = {a: 1, c: true, d: 1.1}; // Error !!
var b13: $Exact<A & B> = {a: 1, b: 'hoge', c: true, d: 1.1}; // Error !!

// Diff type
var c00: $Exact<$Diff<A, B>> = {}; // Error !!
var c01: $Exact<$Diff<A, B>> = {a: 1}; // Error !!
var c02: $Exact<$Diff<A, B>> = {b: 'hoge'}; // Error !!
var c03: $Exact<$Diff<A, B>> = {c: true}; // Error !!
var c04: $Exact<$Diff<A, B>> = {d: 1.1}; // Error !!
var c05: $Exact<$Diff<A, B>> = {a: 1, b: 'hoge'}; // Error !!
var c06: $Exact<$Diff<A, B>> = {a: 1, c: true}; // Error !!
var c07: $Exact<$Diff<A, B>> = {a: 1, d: 1.1}; // Error !!
var c08: $Exact<$Diff<A, B>> = {b: 'hoge', c: true}; // Error !!
var c09: $Exact<$Diff<A, B>> = {b: 'hoge', d: 1.1}; // Error !!
var c10: $Shape<$Diff<A, B>> = {a: 1, b: 'hoge', c: true}; // Error !!
var c11: $Exact<$Diff<A, B>> = {a: 1, b: 'hoge', d: 1.1}; // Error !!
var c12: $Exact<$Diff<A, B>> = {a: 1, c: true, d: 1.1}; // Error !!
var c13: $Exact<$Diff<A, B>> = {a: 1, b: 'hoge', c: true, d: 1.1}; // Error !!
```

これはUnion typeの場合は`A`もしくは`B`を満たす型がなくなり。
Intersection type、Diff typeではExactがサポートされていないためエラーになります。
（Intersection typeでは`exact type. Type is incompatible with (unclassified use type: MakeExactT) any member of intersection type`というエラーが発生し、Diff typeでは`exact type. Unsupported exact type`が発生する）

また、Exact type同士でUnion type、Intersection type、Diff typeを作成すると上記までの結果と変わります。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgCCYAvGAN4A+YAhgFxgB2ArgLYBGeATgDRjsMAzhi4BLRgHMwlAL4BuTLgIAhUhWoCwwsZL4BjBuzhwYeGo2nz0wYGACqjOBez5UANxpdaABm8Ni1Kpk5PJgNmAAolxccF4AhHHunj4AjP7SYEEU9GApoeFRMfGJHl403gBM6YFq5JoA5AAWcBJ49fm2hbFgCUll3gDM1Zm1BmAizHgdkdHdvaU+ACzDWeQAJgwpAHR5cmGds8V9PgCsK7U5KXwNza3tCgvlAGznwZf6DBNTewWHPSXJcoAdle2U2fA2uR20y6R0e3gAHKC6gwmi02h9xlxJlZ4QBOZE3dH1CGbaE-A5Ff7HGgpPxEDKrd78VG3DFgMZfGF-eaAlJpBk1N7gllgNF3UlQ3b7GZU3llFJVQUjYW5TFfSXbaW-OUAhVDZVMkVEiUcz7YvCa8ky2HU1DhACSjAw3EEeD0GFETnGSmO7F86QAZCqKNzdX7vALiMGjbkw3M9fxKkGQyixWz7jaeYn-Qbo6nORb43Dkv7lgyY7VIVri9SFv6zhXU8yTW1a-Kky8m7GrmasTiKbKExGQd2LiLq9adcP64iU6tWyS+1zB7aO-6CWPgourdrKTPS3T5+O1aLxezCwOs+H6-zj6rezuwJO90OS152Ir72DT5fLc+yVfNccxSPMwErB9rlZYl1QtXd20Se1bAAEVEKAoB9VwFj0AMwAAElQ9CAB5CD4ZQAD5agQ44cIFAi0KgEiyMoh9qOw5N8MIxjSMyFiKEXNjkhwg16OIniKNGc0r2nd8OW8ctRO45iq0AwSvBwxtFKY3iT0faC7jUuSuy08S+PIZk-0MnDRxM5TIIAqUrLnTiGO0iTt30i8pO+a8D3U7xN1snSPPTGCHJrVdsxoo8XLEuyfz00LTUsyKbyEu9YqU4KEqgpL2RfKyv0ytyzIs7z4NSvyOVAhggvcnKzwzWDJgq3ziiAA)

```js
type A = {| a: number, b: string |};
type B = {| b: string, c: boolean |};

// Unon type
var a00: A | B = {}; // Error !!
var a01: A | B = {a: 1}; // Error !!
var a02: A | B = {b: 'hoge'}; // Error !!
var a03: A | B = {c: true}; // Error !!
var a04: A | B = {d: 1.1}; // Error !!
var a05: A | B = {a: 1, b: 'hoge'};
var a06: A | B = {a: 1, c: true}; // Error !!
var a07: A | B = {a: 1, d: 1.1}; // Error !!
var a08: A | B = {b: 'hoge', c: true};
var a09: A | B = {b: 'hoge', d: 1.1}; // Error !!
var a10: A | B = {a: 1, b: 'hoge', c: true}; // Error !!
var a11: A | B = {a: 1, b: 'hoge', d: 1.1}; // Error !!
var a12: A | B = {a: 1, c: true, d: 1.1}; // Error !!
var a13: A | B = {a: 1, b: 'hoge', c: true, d: 1.1}; // Error !!

// Intersection type
var b00: A & B = {}; // Error !!
var b01: A & B = {a: 1}; // Error !!
var b02: A & B = {b: 'hoge'}; // Error !!
var b03: A & B = {c: true}; // Error !!
var b04: A & B = {d: 1.1}; // Error !!
var b05: A & B = {a: 1, b: 'hoge'}; // Error !!
var b06: A & B = {a: 1, c: true}; // Error !!
var b07: A & B = {a: 1, d: 1.1}; // Error !!
var b08: A & B = {b: 'hoge', c: true}; // Error !!
var b09: A & B = {b: 'hoge', d: 1.1}; // Error !!
var b10: A & B = {a: 1, b: 'hoge', c: true}; // Error !!
var b11: A & B = {a: 1, b: 'hoge', d: 1.1}; // Error !!
var b12: A & B = {a: 1, c: true, d: 1.1}; // Error !!
var b13: A & B = {a: 1, b: 'hoge', c: true, d: 1.1}; // Error !!

// Diff type
var c00: $Diff<A, B> = {}; // Error !!
var c01: $Diff<A, B> = {a: 1}; // Error !!
var c02: $Diff<A, B> = {b: 'hoge'}; // Error !!
var c03: $Diff<A, B> = {c: true}; // Error !!
var c04: $Diff<A, B> = {d: 1.1}; // Error !!
var c05: $Diff<A, B> = {a: 1, b: 'hoge'}; // Error !!
var c06: $Diff<A, B> = {a: 1, c: true}; // Error !!
var c07: $Diff<A, B> = {a: 1, d: 1.1}; // Error !!
var c08: $Diff<A, B> = {b: 'hoge', c: true}; // Error !!
var c09: $Diff<A, B> = {b: 'hoge', d: 1.1}; // Error !!
var c10: $Diff<A, B> = {a: 1, b: 'hoge', c: true}; // Error !!
var c11: $Diff<A, B> = {a: 1, b: 'hoge', d: 1.1}; // Error !!
var c12: $Diff<A, B> = {a: 1, c: true, d: 1.1}; // Error !!
var c13: $Diff<A, B> = {a: 1, b: 'hoge', c: true, d: 1.1}; // Error !!
```

Union typeではExact typeの`A`もしくは`B`と一致するので変数`a5`、`a8`でエラーが出ません。
しかし、Intersection typeでは`A`、`B`共にExact typeのため、それぞれに定義されたプロパティを持つことができません。
そのため`A`から見ると`B`のプロパティ`c`が余分になるためエラーとなり、`B`から見たときは逆にプロパティ`a`が余分になりエラーとなってしまいます。
Diff typeの場合はよくわかりませんが、Exact typeなので除くということが出来ないということだと思います。（たぶん）

# $Exports<'M'>

`$Exports`は`import`や`require`で読み込まれるモジュールの型です。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVATApgYxgQwCcswBbODAVxhIFkwBvVMMbPIk9gZy7AAk4AcxIMAvs1a4CxMN14AxSoPyNx41ADciYfAC4wAEgCiADwAOcQgBcuAHgDktewD4wAXjDEAjpQCWxAApHewBKAG50LUIdAEZ9LitCXwA7QXcdADoBYTCwYGAwI0JCSzAAQjLNbXwAJnjElLSPfAzFZVz8wuLSiqro-ABmeqTU9JaABV8ATzgwoA)

```js
declare module M {
  declare class Hoge {}
  declare class Fuga {}
}

var a: $Exports<'M'> = require('M');

var a1: string = a.Hoge; // Error !!
var a2: string = a.Fuga; // Error !!
var a3: string = a.Piyo;
```

※ 実際には`M`というモジュールは作らないと`require`でもエラーは発生します

少し挙動は特殊で、モジュールに定義された`Hoge`と`Fuga`は定義通りの型となり、違う型の変数に代入するとエラーになります。
しかし、未定義の`Piyo`は`any`扱いで読み込まれるためエラーにはなりません。
このあたりの挙動は

* `/* @flow */`付きのファイルのモジュールを読み込む
* `/* @flow */`のないファイルのモジュール読み込む
* `declare module`で定義されたモジュールを読み込む
* 存在しないファイルを読み込む

の組み合わせ方で挙動が変わります。

まぁ、普通に`import`を使っていれば`$Exports`は使わないので、そのあたりの検証は割愛します。

# $Abstract<T>

https://flowtype.org/docs/utility-types.html#abstractt

`$Abstract`は`T`型を抽象型に変換する型です。

[# Try Flow](https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVBjGBDAzrsAQQB4AVAPjAG9UwxcAXbBgSwzGwC4xSBuVAL6Yc+MACEylGnUbM2HbgBJCAI0YAnbBgaT+Q1IQB02QysMZ+Y46fO8wwYGACi69XHVgAhEA)

```js
class A<T> {
  static a: T;
}
class B<T> {
  static a: $Abstract<T>;
}

A.a.b.c;
B.a.b.c; // Error !
```

上記のように型パラメーターを取るクラスで、`static`なプロパティに受け取った型パラメーターを指定すると、パラメータを渡さずに呼び出したさいにそのプロパティは`any`（もしくは`unknown`）として判定されてしまいます。
しかし、それでは困るといった場合に直接のアクセスを防ぐために`$Abstract`を使用して、アクセスできないように抽象型に変換します。

https://github.com/facebook/flow/blob/master/lib/react.js#L19

これはReactの型定義を書くときに利用するのが一般的な使い方のようです。

# おわりに

magic typeはおもしろいですね！！
最近、JSらしいコードを残しながら型安全を目指すにはどうすれば良いんだろ・・・？と思っていたのですが、`$Either`、`$All`、`$Diff`、`$Shape`、`$Exact`と`Object`を操作する型の充実ぶりを見るとこれだ！！という感じがあります。
表現力高いし、推論もなかなか強く、JSのエコシステムにそのまま乗れて便利なので、もっとflowtype人口が増えてくれればいいのに。

--

[その2](http://qiita.com/kinzal/items/c55f81e6af1a22a4763f)に続く。
[その3](http://qiita.com/kinzal/items/8e2978be3ec78663c70b)もできました。