定数に対してFlowの型を設定する

定数に対してFlowの型を設定する場合、工夫というか、トリッキーな書き方をしないと上手く動かない。
業務で必要になり調べ、GitHubのIssueで解決策を見つけたのだが、日本語の資料や記事は見当たらなかった。
ちょっとした小ネタだが、誰かの役に立つかもしれないし、自分の備忘録も兼ねて書いておく。

Flowのバージョンは0.46.0で、動作確認をしている。

まず、何も考えず普通に書いてみる。

// @flow

const NORMAL_MEMBER_CODE = 'qwerty';
const ADMIN_CODE = 'admin';

type Code = NORMAL_MEMBER_CODE | ADMIN_CODE;

function sendCode(code: Code): void {
  // 何らかの処理
  console.log(code);
}

sendCode('admin');

最初に、NORMAL_MEMBER_CODEADMIN_CODEという2つの定数を定義している。
sendCodeの引数は必ずCodeという型であり、これは、NORMAL_MEMBER_CODEADMIN_CODEのいずれかである。 何もおかしなところはない。

しかしこれをFlowでチェックすると、エラーになる。

6: type Code = NORMAL_MEMBER_CODE | ADMIN_CODE;
               ^^^^^^^^^^^^^^^^^^ string. Ineligible value used in/as type annotation (did you forget 'typeof'?)
6: type Code = NORMAL_MEMBER_CODE | ADMIN_CODE;
               ^^^^^^^^^^^^^^^^^^ NORMAL_MEMBER_CODE


6: type Code = NORMAL_MEMBER_CODE | ADMIN_CODE;
                                    ^^^^^^^^^^ string. Ineligible value used in/as type annotation (did you forget 'typeof'?)
6: type Code = NORMAL_MEMBER_CODE | ADMIN_CODE;
                                    ^^^^^^^^^^ ADMIN_CODE

(did you forget 'typeof'?)と出ているので、Codeの定義を以下のように書き換えてみる。

type Code = typeof NORMAL_MEMBER_CODE | typeof ADMIN_CODE;

これなら確かに、エラーは出ないようになる。
しかしこれは、Codestringですと定義しているだけであり、文字列なら何でも通ってしまう。
例えばsendCode('hoge');でも、Flowはエラーを出さない。
qwertyadmin以外ではエラーを出すのが、意図する挙動である。

正解は、以下。

// @flow

const NORMAL_MEMBER_CODE: 'qwerty' = 'qwerty';
const ADMIN_CODE: 'admin' = 'admin';

type Code = typeof NORMAL_MEMBER_CODE | typeof ADMIN_CODE;

function sendCode(code: Code): void {
  // 何らかの処理
  console.log(code);
}

// sendCode('hoge'); // Found 1 error
sendCode('admin'); // No errors!

これで、正しく動くようになり、qwertyadmin以外ではエラーを出すようになった。

参考資料