跳至主要內容

變數宣告

在宣告新變數時,您可以選擇宣告其類型。

JavaScript 有三種宣告局部變數的方法

  • var - 宣告變數,並可選擇指定值。(MDN
  • let - 宣告區塊範圍變數,並可選擇指定值。(MDN
  • const - 宣告區塊範圍變數,並指定一個無法重新指定的值。(MDN

在 Flow 中,這些方法分為兩組

  • letvar - 可以重新指定值的變數。
  • 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;

varlet 0.186

由於 varlet 可以重新指派,因此您需要了解一些其他規則。

當您提供類型時,您將能夠重新指派值,但它必須始終是相容的類型。

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]

如果由於 ifswitch 陳述式而有兩個或更多可能的「第一個指派」,它們都將計算在內—這是 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

您可以選擇使用 mixedany 準確地註解它。例如

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 變數的預設類型。