LoginSignup
5
2

More than 5 years have passed since last update.

flowtypeで始めるsealed case pattern

Last updated at Posted at 2018-09-23

皆さん、こんにちは!!
ちょっと社内でScalaのsealed pattern matchみたいに全ての型を網羅できるか検証する方法が話題になったのでまとめておきます。

元ネタに関しては上記2つのissueをご参照ください。

ユースケース

例えば下記のようにA | BというUnion Typeを全網羅するswitchを書いたときに、このswitchが全網羅されていることを担保したい時がありませんか?
仮にA | B | CのようにUnion Typeが追加されたさいにCが新しいケースとして追加されるべきなのに、そこの対応を漏らしてdefaultになってしまっていたとかよくある話だと思います。

type A = { type: 'A' };
type B = { type: 'B' };

declare var o: A | B;

switch (o.type) {
  case 'A':
    // ...
    break;
  case 'B':
    // ...
    break;
  default: // ...
}

そういった問題を防ぐためにsealedして新しい型が追加されたときにケースの追加漏れを防ぎます。

sealed case

type A = { type: 'A' };
type B = { type: 'B' };
type C = { type: 'C' };

declare var o: A | B;

switch (o.type) {
  case 'A':
    console.log(`type A == ${o.type}`);
    break;
  case 'B':
    console.log(`type B == ${o.type}`);
    break;
  default: (o:empty)
}

Try Flow

やり方は簡単でdefaultケースにて(o:empty)emptyにキャストするだけです。簡単ですね!!
仮にdeclare var o: A | B; | CのようにCを追加すると、emptyへのキャストでエラーが出るようになります。

何故、このような動きをするのかというとType Refinementが関わってきます。
今回の場合で言うとcase 'A'のステートメントではoA型としてType Refinementされます。
逆に言えばその他のswitch内のステートメントではA | BからAを除いたB型であるといえます。
次のcase 'B'のステートメントではoB型としてType Refinementされ、最後に残ったdefaultのステートメントではA | BからABを除いた何にもマッチしない型(=empty)であるとType Refinementされることになります。
そのため、defaultのステートメントではo:emptyというキャストが成功することになります。

しかし、A | B | Cの場合はどうでしょうか?
A | B | CからABを除いた型はCになるため(o:empty)Cemptyにキャストしようとして失敗します。

蛇足

蛇足話ですが、A | BからAを除外とか言うと$Diff<A, B>のことを思い出しますが、$Diff<A, B>を使っても同じようなことはできません。
$Diff<A, B>ABObject型を指定して、AのプロパティからBのプロパティを除外するための型なので、同じことはできません。

さらに蛇足話ですが、A & Bの場合はどちらも満たす型なので、常に一つめのケースにマッチします。
(というか今回のケースだとA & Bを満たす型の値は絶対に作れないのでアレですけど)

まとめ

と言う訳でいかがでしたでしょうか。
昨今のJSではよくstring literalのenumを作ることが多いので結構登場頻度の多い技術かと思います。
$Keys$Values$ElementTypeなどenumと組み合わせて使えるUtility Typeもあるので、sealedと組み合わせてより良き型安全の世界を楽しんでください。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2