flowtype v0.31.0がでましたね!(既にv0.32.0もでてますけど)
CHANGELOG読んでみると
Added a new "magic" type called $PropertyType. This utility extracts the type of the property 'x' off of the type T.
magic typeなる気になる用語があるじゃありませんか!!
どうやら$
から始まる定義済みの型のことを指してるようなのですが、公式のドキュメントや過去のCHANGELOGを見てもそんなものは一切出てきません。
仕方ないので実際にコードを見てみると
type_annotation.ml
の中に$
から始まる型があるので、名前とコメントから推測しつつ、flowtype.orgのtryで確認しながらどういった型なのか調べてみました。
ちなみにですが、flowtypeのドキュメント的にはUtility Typeというが正しいようです。(2017/01/07追記)
$Either<...T>
$Either
は|
と等価で指定した型のいずれかを表す型(Union type)になります。
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を使用してください。
/* @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で判別することで、type
がhoge
のA
ならプロパティnum
が利用できるようになり、type
がfuga
のB
ならプロパティstr
が利用できるようになります。
$All<...T>
$All
は&
と等価で指定した型の全てと一致する型(Intersection Type)になります。
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
のキーはそのまま利用することができます。
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
である必要がある)
~~```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
$Subtype
はclass Klass<+T> {}
と同様に共変を表す型です。
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
$Type
はT
と一致した型を取る型です。
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
$PropertyType
はT
に指定した型のプロパティx
の型を取得する型です。
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
$NonMaybeType
はT
に指定したMaybe Typeをnull
やundefined
を許可しない元の型に戻す型です。
type A = ?string;
var a1: A = 'hoge';
var a2: A = null;
var b1: $NonMaybeType<A> = 'fuga';
var b2: $NonMaybeType<A> = null; // Error !!
$Shape
$Shape
はT
に指定した型の部分型であり、Object
とは違いT
と一致しないプロパティを許容しない型になります。
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と組み合わせると、それぞれで許容される型が変わります。
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
$Diff
はT
からS
を除外した型を許容する型(Diff type)になります。
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 | $Enum
$Keys
はT
に指定した型のキーのUnion typeになります。
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
$Exact
はT
に指定した型の同型のみを許容する型になります。
v0.3.2からは構文が導入され{| |}
で表現可能になっています。
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と組み合わせると代入不可能な型になります。
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を作成すると上記までの結果と変わります。
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
で読み込まれるモジュールの型です。
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
$Abstract
はT
型を抽象型に変換する型です。
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
を使用して、アクセスできないように抽象型に変換します。
これはReactの型定義を書くときに利用するのが一般的な使い方のようです。
おわりに
magic typeはおもしろいですね!!
最近、JSらしいコードを残しながら型安全を目指すにはどうすれば良いんだろ・・・?と思っていたのですが、$Either
、$All
、$Diff
、$Shape
、$Exact
とObject
を操作する型の充実ぶりを見るとこれだ!!という感じがあります。
表現力高いし、推論もなかなか強く、JSのエコシステムにそのまま乗れて便利なので、もっとflowtype人口が増えてくれればいいのに。
--