Flow を使い始めて、気になったりハマったりで解決に多少の時間を要した点のメモ集です。
マニュアルを読めば解決していたなど、普通大丈夫だろみたいな点も含まれていると思います。
バージョンは、特に注釈をしていない場合は v0.40.0
であるものとします。
随時追加予定です。
環境設定
設定しないですぐ試してみたい
Try Flow という公式の Web サービスを使えば、今すぐ試すことができます。
"flow; test $? -eq 0 -o $? -eq 2"
は何ですか
"flow; test $? -eq 0 -o $? -eq 2"
は何ですか過去バージョンの Getting Started で用意されていた npm スクリプトでした。
v0.43.1
では "flow": "flow"
に変わっていました。
シェル上で実行したい
flow check-contents
を使います。
echo 'var s: string = ""; s = 1;' | flow check-contents
-:1
1: var s: string = ""; s = 1;
^ number. This type is incompatible with
1: var s: string = ""; s = 1;
^^^^^^ string
Found 1 error
[libs]
は ./〜
では指定できない
False)
[libs]
./decls/
True)
[libs]
decls/
マニュアル該当箇所) https://flowtype.org/docs/advanced-configuration.html#libs
"These paths can be relative to the root directory or absolute." と書いてあるのでてっきり "./" スタートなのかと思いこんでいていました。
".js.flow" style
の declarations が反映されない
False)
// @flow
/* Your JS code.. */
// @flow
/* Your Flow code.. */
True)
/* Your JS code.. */
// @flow
/* Your Flow code.. */
マニュアル該当箇所) https://flowtype.org/docs/declarations.html#declaration-files
// @flow
は .js
側には記載してはいけません。
[include]
や [libs]
に新しく置いたファイルが認識されない
flow サーバを一度止めましょう。
# 止める
flow stop
# 次の実行から読み込まれる
flow
型アノテーション
Sealed object types
Object 型のアノテーションを付与すると、「存在しないプロパティへアクセスするとエラーをレポートする」状態になります。
これを、Sealed object types と呼ぶようです。1
例として、あるプレーンオブジェクトの存在しない x
プロパティへアクセスするコードを、型アノテーションの有り無しそれぞれの状況で Flow でチェックしてみましょう。
型アノテーション無し)
echo 'const obj = {}; obj.x; obj.x = 1;' | flow check-contents
No errors!
型アノテーション有り)
echo 'const obj: {} = {}; obj.x; obj.x = 1;' | flow check-contents
-:1
1: const obj: {} = {}; obj.x; obj.x = 1;
^ property `x`. Property not found in
1: const obj: {} = {}; obj.x; obj.x = 1;
^^^ object type
-:1
1: const obj: {} = {}; obj.x; obj.x = 1;
^ property `x`. Property not found in
1: const obj: {} = {}; obj.x; obj.x = 1;
^^^ object type
Found 2 errors
後者は参照と代入それぞれでエラーになりました。
しかし、型アノテーションが無い場合でも有りと同じ状態になることがあります。
それは、中身が空ではないプレーンオブジェクトを定義した場合です。
型アノテーションは無いが空ではないプレーンオブジェクトを宣言した場合)
echo 'const obj = { y: 2 }; obj.x; obj.x = 1;' | flow check-contents
-:1
1: const obj = { y: 2 }; obj.x; obj.x = 1;
^ property `x`. Property not found in
1: const obj = { y: 2 }; obj.x; obj.x = 1;
^^^ object literal
-:1
1: const obj = { y: 2 }; obj.x; obj.x = 1;
^ property `x`. Property not found in
1: const obj = { y: 2 }; obj.x; obj.x = 1;
^^^ object literal
Found 2 errors
さて、この仕組が問題になりやすい状況として、Object.assign
によるプロパティの拡張があります。
また例を出します)
%echo 'Object.assign({ x: 1 }, { y: 2 });' | flow check-contents
-:1
1: Object.assign({ x: 1 }, { y: 2 });
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ property `y` of object literal. Property not found in
1: Object.assign({ x: 1 }, { y: 2 });
^^^^^^^^ object literal
Found 1 error
これがエラーになるのは、第一引数のプレーンオブジェクトが空ではないため sealed として認識されているのに、第二引数で拡張しているからです。
この場合は、第一引数を {}
にすると解決します:
$echo 'Object.assign({}, { x: 1 }, { y: 2 });' | flow check-contents
No errors!
ロジックは微妙に変わってしまいますが、個人的には許容範囲内かなと感じています。
参考 Issue) Object.assign({}, objOfType) causes property not found in object literal
error #1805
Union や Intersection を使ったら意図せぬエラーがでる
自分の場合、Union と Intersection に対する理解がそもそも誤っていました。
解決に当っては、以下の記事が参考になりました。
関数オーバーロードとIntersection types
一文を引用しておきます。
どちらの型が入っているかわからないもの(Union types)に対して呼び出すには、どちらの型が入っていても問題ないように呼び出し可能かチェックする部分で引っかかっていたわけです。
関数の戻り値に対する定義を確認する際のショートハンド
手元で関数の戻り値に対する定義の動作確認をする際に、例えば以下のように書く代わりに、
echo 'const x: string = Math.random()' | flow check-contents
-:1
1: const x: string = Math.random()
^^^^^^^^^^^^^ number. This type is incompatible with
1: const x: string = Math.random()
^^^^^^ string
Found 1 error
この様に書くこともできます。
echo '(Math.random(): string)' | flow check-contents
-:1
1: (Math.random(): string)
^^^^^^^^^^^^^ number. This type is incompatible with
1: (Math.random(): string)
^^^^^^ string
Found 1 error
じゃあ、括弧も省略できそうじゃん!とやってみると構文エラーです。
echo 'Math.random(): string' | flow check-contents
-:1
1: Math.random(): string
^ Unexpected token :
Found 1 error
公式ドキュメントを眺めていて知った記法なのですが、何故こうなるのかは不明です。