変数宣言
新しい変数を宣言するとき、オプションでその型を宣言できます。
JavaScriptには、ローカル変数を宣言する3つの方法があります。
var
- 変数を宣言し、オプションで値を割り当てます。(MDN)let
- ブロックスコープの変数を宣言し、オプションで値を割り当てます。(MDN)const
- ブロックスコープの変数を宣言し、再割り当てできない値を割り当てます。(MDN)
Flowでは、これらは2つのグループに分類されます。
let
とvar
- 再割り当て可能な変数。const
- 再割り当て不可能な変数。
1var varVariable = 1;2let letVariable = 1;3const constVariable = 1;4
5varVariable = 2; // Works!6letVariable = 2; // Works!7constVariable = 2; // Error!
7:1-7:13: Cannot reassign constant `constVariable` [1]. [reassign-const]
const
const
変数は後で再割り当てできないため、非常に簡単です。
Flowは、割り当てている値から型を推論するか、型を提供できます。
1const foo /* : number */ = 1;2const bar: number = 2;
var
と let
≥0.186
var
とlet
は再割り当てできるため、知っておく必要のあるルールがいくつかあります。
型を提供する場合、値を再割り当てできますが、常に互換性のある型である必要があります。
1let foo: number = 1;2foo = 2; // Works!3foo = "3"; // Error!
3:7-3:9: Cannot assign `"3"` to `foo` because string [1] is incompatible with number [2]. [incompatible-type]
変数に注釈がない場合、Flowはその初期化子または最初の割り当てに基づいて正確な型を推論します。その変数への後続の割り当てはすべて、この型によって制約されます。このセクションでは、Flowが注釈のない変数に推論する型を決定する方法の例をいくつか示します。
変数の型をFlowが推論する型と異なる型にしたい場合は、常に変数の宣言に型注釈を追加できます。これにより、このページで説明したすべてがオーバーライドされます!
宣言時に初期化された変数
注釈のない変数の一般的なケースは非常に簡単です。変数がリテラルnull
ではない初期化子で宣言されている場合、その変数にはそれ以降、初期化子の型が設定され、変数への将来の書き込みはその型によって制約されます。
1import * as React from 'react';2
3type Props = $ReadOnly<{ prop: string }>;4
5declare var x: number;6declare var y: number;7declare var props: Props;8
9let product = Math.sqrt(x) + y;10// `product` has type `number`11
12let Component = ({prop}: Props): React.Node => { return <div/> }13// `Component` has type`React.ComponentType<Props>`14
15let element = <Component {...props} />16// `element` has type `React.Element<React.ComponentType<Props>>`17
18/* Let's define a new component */19
20type OtherProps = $ReadOnly<{ ...Props, extra_prop: number }>;21declare var OtherComponent: (OtherProps) => React.Node;22declare var other_props: OtherProps23
24/* Any subsequent assignments to `product`, `Component`, or `element` will be25 * checked against the types that Flow infers for the initializers, and if26 * conflicting types are assigned, Flow will signal an error. */27
28product = "Our new product is..."; 29Component = ({prop}: OtherProps): React.Node => { return <div/> }; 30element = <OtherComponent {...other_props} />;
28:11-28:33: Cannot assign `'Our new pr...'` to `product` because string [1] is incompatible with number [2]. All writes to `product` must be compatible with the type of its initializer [3]. Add an annotation to `product` [3] if a different type is desired. [incompatible-type]29:13-29:65: Cannot assign function to `Component` because property `extra_prop` is missing in object type [1] but exists in object type [2] in the first parameter. All writes to `Component` must be compatible with the type of its initializer [3]. Add an annotation to `Component` [3] if a different type is desired. [prop-missing]30:11-30:45: Cannot assign `<OtherComponent />` to `element` because property `extra_prop` is missing in object type [1] but exists in object type [2] in type argument `P` [3]. All writes to `element` must be compatible with the type of its initializer [4]. Add an annotation to `element` [4] if a different type is desired. [prop-missing]30:12-30:25: Cannot assign `<OtherComponent />` to `element` because property `extra_prop` is missing in object type [1] but exists in object type [2] in the first parameter of type argument `ElementType` [3]. All writes to `element` must be compatible with the type of its initializer [4]. Add an annotation to `element` [4] if a different type is desired. [prop-missing]
これらの例を型チェックし、Flowがこれらの変数にさまざまな種類の値を書き込むことができることを認識させるには、これらの宣言にこのより一般的な型を反映する型注釈を追加する必要があります。
let product: number | string = ...
let Component: mixed = ... // No good type to represent this! Consider restructuring
let element: React.Node = ...
初期化子なしで宣言された変数
多くの場合、変数は初期化子なしで宣言されます。このような場合、Flowは変数の型を定義するために、変数への「最初の」割り当てまたは割り当てを選択しようとします。「最初」とは、上から下へ、かつより近いスコープからより深いスコープへの両方を意味します。変数の宣言と同じ関数スコープで発生する割り当てを選択しようとし、ローカルで割り当てが見つからない場合にのみ、ネストされた関数の中を調べます。
1let topLevelAssigned;2function helper() {3 topLevelAssigned = 42; // Error: `topLevelAssigned` has type `string` 4}5topLevelAssigned = "Hello world"; // This write determines the var's type6topLevelAssigned = true; // Error: `topLevelAssigned` has type `string`
3:22-3:23: Cannot assign `42` to `topLevelAssigned` because number [1] is incompatible with string [2]. All writes to `topLevelAssigned` must be compatible with the type of its initial assignment [3]. Add an annotation to `topLevelAssigned` [4] if a different type is desired. [incompatible-type]6:20-6:23: Cannot assign `true` to `topLevelAssigned` because boolean [1] is incompatible with string [2]. All writes to `topLevelAssigned` must be compatible with the type of its initial assignment [3]. Add an annotation to `topLevelAssigned` [4] if a different type is desired. [incompatible-type]
if
ステートメントまたはswitch
ステートメントが原因で、複数の可能な「最初の割り当て」がある場合、両方がカウントされます。これは、Flowが変数型の和集合を推論する数少ない方法の1つです。
1let myNumberOrString;2declare var condition: boolean;3if (condition) {4 myNumberOrString = 42; // Determines type5} else {6 myNumberOrString = "Hello world"; // Determines type7}8myNumberOrString = 21; // fine, compatible with type9myNumberOrString = "Goodbye"; // fine, compatible with type10myNumberOrString = false; // Error: `myNumberOrString` has type `number | string`
10:20-10:24: Cannot assign `false` to `myNumberOrString` because: [incompatible-type] Either boolean [1] is incompatible with number [2]. Or boolean [1] is incompatible with string [3]. All writes to `myNumberOrString` must be compatible with the type of one of its initial assignments [4], [5]. Add an annotation to `myNumberOrString` [6] if a different type is desired.
ただし、これは変数が両方の分岐に書き込まれる場合にのみ適用されます。一方の分岐にのみ書き込みが含まれている場合、その書き込みが後で変数の型になります(ただし、Flowは変数が間違いなく初期化されていることを確認するためにチェックします)。
1let oneBranchAssigned;2declare var condition: boolean;3if (condition) {4 oneBranchAssigned = "Hello world!";5}6oneBranchAssigned.toUpperCase(); // Error: `oneBranchAssigned` may be uninitialized 7oneBranchAssigned = 42; // Error: `oneBranchAssigned` has type `string`
6:19-6:29: Cannot call `oneBranchAssigned.toUpperCase` because property `toUpperCase` is missing in possibly uninitialized variable [1]. [incompatible-use]7:21-7:22: Cannot assign `42` to `oneBranchAssigned` because number [1] is incompatible with string [2]. All writes to `oneBranchAssigned` must be compatible with the type of its initial assignment [3]. Add an annotation to `oneBranchAssigned` [4] if a different type is desired. [incompatible-type]
null
に初期化された変数
最後に、変数の型が最初の割り当てによって決定されるという一般的な原則の1つの例外は、変数がリテラル値null
として初期化される場合(または最初の割り当てがnull
の場合)です。このような場合、(上記と同じルールを使用して)次のnull以外の割り当てが変数の残りの型を決定し、変数の全体的な型はnull
と後続の割り当ての型の和集合になります。これは、変数が他の型の値によって割り当てられる前にnull
として開始するという一般的なパターンをサポートします。
1function findIDValue<T>(dict: {[key: string]: T}): T {2 let idVal = null; // initialized as `null`3 for (const key in dict) {4 if (key === 'ID') {5 idVal = dict[key]; // Infer that `idVal` has type `null | T`6 }7 }8 if (idVal === null) {9 throw new Error("No entry for ID!");10 }11 return idVal;12}
catch変数 ≥0.197
catch
変数に注釈がない場合、そのデフォルト型はany
です。
オプションで、mixed
またはany
で正確に注釈を付けることができます。例:
1try {2} catch (e: mixed) {3 if (e instanceof TypeError) {4 e as TypeError; // OK5 } else if (e instanceof Error) {6 e as Error; // OK7 } else {8 throw e;9 }10}
mixed
を使用すると、ランタイムチェックが増加するというトレードオフで、安全性とFlowのカバレッジを向上させることができます。
use_mixed_in_catch_variables
オプションをtrueに設定することで、注釈がない場合のcatch
変数のデフォルト型を変更できます。