變數宣告
在宣告新變數時,您可以選擇宣告其類型。
JavaScript 有三種宣告局部變數的方法
在 Flow 中,這些方法分為兩組
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 仍會為變數類型推斷聯集的少數方法之一
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 的變數
最後,變數類型由其第一次指定決定的一般原則有一個例外,即當變數初始化為(或其第一次指定為)文字值 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}捕捉變數 ≥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 變數的預設類型。