ReactJS - 靜態型別檢查



由於 JavaScript 是一種動態型別語言,因此在執行程式碼之前很難發現型別不匹配錯誤。React 透過 prop-types 包支援對 props 進行型別檢查,可以在開發階段使用它來識別屬性的型別不匹配。

程式的其他方面仍然需要一個工具來在開發階段正確識別型別問題。JavaScript 有很多靜態型別檢查器工具來處理型別問題。我們將檢查兩個流行的選項,它們可以平滑地整合到 React 應用程式的工作流程中,並在應用程式的開發階段提供可能的型別錯誤提示。它們如下所示:

  • Flow

  • TypeScript 語言

Flow

Flow 是 JavaScript 的靜態型別檢查器。Flow 擴充套件了 JavaScript 語言以指定型別,並允許在 JavaScript 程式碼中設定靜態型別註釋。Flow 將檢查開發人員在程式碼中設定的靜態型別註釋,並確保使用了正確的型別。否則,它將丟擲錯誤。一個簡單的例子如下所示:

// @flow
function sum(a: number, b: number) : number {
   return a + b;
}
sum(10, 20)     // Pass
sum("10", "20") // Fail

這裡,// @flow 註釋使 Flow 靜態檢查器能夠分析下面定義的函式。如您所見,函式 sum 使用 Flow 語言擴充套件來指定引數的型別。讓我們看看如何在 React 專案中啟用 Flow 以及啟用 Flow 的 React 專案的開發工作流程。

步驟 1 - 使用 create-react-app CLI 應用建立一個新的 React 專案。

create-react-app myapp

步驟 2 - 使用以下命令將 Flow 新增到專案中

cd myapp
npm install --save-bin flow-bin

步驟 3 - 現在,在 package.json 檔案的 scripts 中新增 Flow 命令

{
   // ...
   "scripts": {
      "flow": "flow",
      // ...
   },
   // ...
}

這將允許我們透過 npm 執行 flow 命令

步驟 4 - 使用以下命令初始化 Flow 配置:

npm run flow init

這將在專案的根目錄建立一個基本的 Flow 配置檔案 .flowconfig,其內容如下。

[ignore]
[include]
[libs]
[lints]
[options]
[strict]

可以在這裡新增高階 Flow 選項。預設情況下,Flow 將檢查我們應用程式中的所有檔案。要忽略 node_modules,請在 [ignore] 選項下新增 .*/node_modules/.*。這將指示 Flow 應用程式忽略 node_modules 資料夾中的所有檔案。

[ignore]
.*/node_modules/.*
[include]
[libs]
[lints]
[options]
react.runtime=automatic
[strict]

步驟 5 - 現在,我們已將 Flow 配置到我們的應用程式中。我們可以在我們的程式碼中使用 Flow 註釋,並使用以下命令針對 Flow 進行測試

npm run flow

Flow 將檢查我們的程式碼並在控制檯中顯示類似以下的結果:

> myapp@0.1.0 flow /path/to/myapp
> flow
Launching Flow server for /path/to/myapp
Spawned flow server (pid=1629)
Logs will go to /private/tmp/flow/zSUserszSbalazSProjectszSArticleszSreact-revision-v2zSworkspacezSmyapp.log
Monitor logs will go to /private/tmp/flow/zSUserszSbalazSProjectszSArticleszSreact-revision-v2zSworkspacezSmyapp.monitor_log
No errors!

步驟 6 - 現在,可以使用 Flow 註釋在我們的程式碼中。讓我們在我們的程式碼中新增一個簡單的 Flow 註釋,並執行 flow 命令來檢查程式碼的正確性。建立一個簡單的 HelloWorld 元件(src/components/HelloWorld.js),如下所示:

import React from 'react'
class HelloWorld extends React.Component {
   render() {
      return <h1>Hello, {this.props.name}</h1>
   }
}
export default HelloWorld

步驟 7 - 將元件包含在我們的根元件(App.js)中,如下所示:

// @flow
import React from "react";
import HelloWorld from "./components/HelloWorld";
function App() : any {
   var name: string = 10
   return (
      <div>
         <HelloWorld name={name} />
      </div>
   )
}
export default App;

步驟 8 - 現在,使用 flow 檢查程式碼,如下所示

npm run flow

Flow 命令將檢查程式碼並顯示 name 設定為字串值的錯誤,如下所示。

> myapp@0.1.0 flow /path/to/myapp
> flow
Error ............................................src/App.js:6:22
Cannot assign 10 to name because number [1] is incompatible with string [2]. [incompatible-type]
      3│ import HelloWorld from "./components/HelloWorld";
      4│
      5│ function App() : any {
[2][1]6│   var name: string = 10
      7│
      8│   return (
      9│     <div>
Found 1 error
.....
.....

步驟 9 - 讓我們透過為 name 變數提供一個字串值來修復錯誤,並重新執行 flow 命令。

// @flow
import React from "react";
import HelloWorld from "./components/HelloWorld";
function App() : any {
   var name: string = "John"
   return (
      <div>
         <HelloWorld name={name} />
      </div>
   )
}
export default App;

現在,flow 命令將成功並顯示程式碼中沒有問題。

> myapp@0.1.0 flow /path/to/myapp
> flow
No errors!

步驟 10 - 最後,我們可以透過執行以下命令來執行應用程式,

npm start

可以使用以下命令建立最佳化的生產版本,

npm run build

TypeScript

TypeScript 是一種對靜態型別具有一流支援的語言。靜態型別使 TypeScript 能夠在編譯時而不是執行時捕獲型別錯誤。TypeScript 支援 JavaScript 的所有語言特性。

因此,對於 JavaScript 開發人員來說,使用 TypeScript 非常容易。React 對 TypeScript 具有內建支援。要建立 React 專案,只需在透過 create-react-app 建立 React 應用期間包含 TypeScript 模板即可。

create-react-app myapp --template typescript

建立應用程式後,在 src/components 資料夾下新增一個新的 HelloWorld 元件 (HelloWorld.tsx),如下所示:

// src/components/HelloWorld.tsx
import React from 'react'
class HelloWorld extends React.Component {
   render() {
      return <h1>Hello, {this.props.name}</h1>
   }
}
export default HelloWorld

現在,包含 HelloWorld 元件 -

import React from "react";
import HelloWorld from "./components/HelloWorld";
function App() : any {
   var name: string = 10
   return (
      <div>
         <HelloWorld name={name} />
      </div>
   )
}
export default App;

在這裡,我們故意將 name 的值設定為 10。讓我們執行應用程式並檢查編譯器是否捕獲錯誤。

npm start

執行該命令會丟擲錯誤,如下所示:

...
...
ERROR in src/App.tsx:5:7
TS2322: Type 'number' is not assignable to type 'string'.
     3 |
     4 | function App() : any {
   > 5 |   var name: string = 10
       |       ^^^^
     6 |
     7 |   return (
     8 |     <div>
...
...

讓我們更改 name 的值並設定一個字串值("John")。上述錯誤已修復,但編譯器仍在 HelloWorld 元件中丟擲錯誤,如下所示:

Issues checking in progress...
ERROR in src/App.tsx:9:19
TS2769: No overload matches this call.
   Overload 1 of 2, '(props: {} | Readonly<{}>): HelloWorld', gave the following error.
      Type '{ name: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<HelloWorld> & Readonly<{}>'.
         Property 'name' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<HelloWorld> & Readonly<{}>'.
   Overload 2 of 2, '(props: {}, context: any): HelloWorld', gave the following error.
      Type '{ name: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<HelloWorld> & Readonly<{}>'.
         Property 'name' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<HelloWorld> & Readonly<{}>'.
     7 |   return (
     8 |     <div>
> 9 |       <HelloWorld name={name} />
       |                   ^^^^
    10 |     </div>
    11 |   )
    12 | }
ERROR in src/components/HelloWorld.tsx:9:39
TS2339: Property 'name' does not exist on type 'Readonly<{}>'.
     7 | class HelloWorld extends React.Component {
     8 |     render() {
> 9 |        return <h1>Hello, {this.props.name}</h1>
       |                                       ^^^^
    10 |     }
    11 | }
    12 |

要修復錯誤,應為 HelloWorld 元件提供其屬性的型別資訊。為屬性建立一個新的介面,然後將其包含在 HelloWorld 元件中,如下所示:

import React from 'react'
interface HelloWorldProps {
   name: string
}
class HelloWorld extends React.Component<HelloWorldProps> {
   render() {
      return <h1>Hello, {this.props.name}</h1>
   }
}
export default HelloWorld

最後,我們的程式碼在沒有錯誤的情況下編譯,如下所示,並且可以透過 `https://:3000/` 檢視。

No issues found.
Static Type Checking
廣告