使用 TypeScript
TypeScript 是一種為 JavaScript 程式碼庫添加類型定義的流行方式。開箱即用,TypeScript支援 JSX,並且您可以透過在專案中添加 @types/react 和 @types/react-dom來獲得完整的 React Web 支援。
安裝
所有生產級別的 React 框架都支援使用 TypeScript。請遵循框架特定的安裝指南:
在現有的 React 專案中添加 TypeScript
要安裝最新版本的 React 類型定義:
npm install --save-dev @types/react @types/react-dom您需要在 tsconfig.json中設定以下編譯器選項:
dom必須包含在 lib中(注意:如果未指定lib選項,則預設會包含dom)。- jsx必須設定為其中一個有效選項。對於大多數應用程式,
preserve應該就足夠了。 如果您要發布函式庫,請查閱 jsx 文件以了解應選擇哪個值。
TypeScript 與 React 元件
注意
每個包含 JSX 的檔案都必須使用.tsx副檔名。這是 TypeScript 特有的副檔名,它告訴 TypeScript 該檔案包含 JSX。
使用 TypeScript 編寫 React 與使用 JavaScript 編寫 React 非常相似。使用元件時的關鍵區別在於,您可以為元件的 props 提供類型。這些類型可用於正確性檢查,並在編輯器中提供內聯文件。
以 MyButton 元件為例,取自快速入門指南,我們可以添加一個描述按鈕title 的類型:
注意
這些沙盒可以處理 TypeScript 程式碼,但它們不執行類型檢查器。這意味著您可以修改 TypeScript 沙盒來學習,但您不會收到任何類型錯誤或警告。要進行類型檢查,您可以使用TypeScript Playground或使用功能更全面的線上沙盒。
這種內聯語法是為元件提供類型的最簡單方式,但一旦您開始需要描述多個欄位,它可能會變得笨拙。相反,您可以使用interface 或 type來描述元件的 props:
描述元件 Props 的型別可以根據需要簡單或複雜,但它們應該是一個使用 type 或 interface 描述的物件型別。你可以在 物件型別中了解 TypeScript 如何描述物件,但你可能也會有興趣使用聯合型別來描述可以是幾種不同型別之一的 prop,以及從型別建立型別指南以獲取更多進階使用案例。
Hook 範例
來自 @types/react的型別定義包含了內建 Hook 的型別,因此你可以在元件中使用它們而無需任何額外設定。它們的設計考量了你在元件中編寫的程式碼,因此你將在大多數情況下獲得推斷型別,理想情況下無需處理提供型別的細節。
然而,我們可以看幾個如何為 Hook 提供型別的範例。
useState
useState Hook 會重用作為初始狀態傳入的值來確定值的型別。例如:
這將為 enabled指派boolean型別,而setEnabled 將是一個接受 boolean 參數或一個回傳 boolean 的函式的函式。如果你想明確地為狀態提供一個型別,可以透過向 useState呼叫提供一個型別參數來實現:
在這個案例中這沒有太大用處,但一個常見的、你可能想要提供型別的情況是當你有一個聯合型別時。例如,這裡的status 可以是幾個不同字串之一:
或者,如 狀態結構化原則中所建議,你可以將相關狀態分組為一個物件,並透過物件型別描述不同的可能性:
useReducer
useReducer Hook是一個更複雜的 Hook,它接受一個 reducer 函式和一個初始狀態。reducer 函式的型別是從初始狀態推斷出來的。你可以選擇性地向useReducer呼叫提供一個型別參數來為狀態提供型別,但通常最好在初始狀態上設定型別:
我們在幾個關鍵地方使用了 TypeScript:
interface State描述了 reducer 狀態的形狀。type CounterAction描述了可以分派給 reducer 的不同操作。const initialState: State為初始狀態提供了類型,同時也是useReducer預設使用的類型。stateReducer(state: State, action: CounterAction): State設定了 reducer 函數參數和回傳值的類型。
為 initialState設定類型的另一種更明確的替代方案是向useReducer提供類型參數:
useContext
useContext Hook是一種無需透過元件傳遞 props 即可將資料沿元件樹向下傳遞的技術。它透過建立一個 provider 元件來使用,並且通常會建立一個 Hook 來在子元件中消費該值。
由 context 提供的值的類型是從傳遞給 createContext呼叫的值推斷出來的:
當你有一個合理的預設值時,這種技術是有效的——但有時你可能沒有,在那些情況下,將 null 作為預設值可能感覺是合理的。然而,為了讓類型系統理解你的程式碼,你需要在 createContext上明確設定ContextShape | null。
這導致了一個問題,即你需要消除 context 消費者類型中的| null。我們的建議是讓 Hook 執行運行時檢查以確認其存在,並在不存在時拋出錯誤:
useMemo
注意
React Compiler 會自動記憶化值和函數,減少了手動 useMemo呼叫的需求。你可以使用編譯器來自動處理記憶化。
useMemoHook 將從函數呼叫建立/重新存取一個記憶化的值,僅當作為第二個參數傳遞的依賴項發生變化時才會重新執行該函數。呼叫 Hook 的結果是從第一個參數中函數的回傳值推斷出來的。你可以透過向 Hook 提供類型參數來更明確地指定。
useCallback
注意
React Compiler 會自動記憶化值和函數,減少了手動 useCallback呼叫的需求。你可以使用編譯器來自動處理記憶化。
useCallback 提供一個函數的穩定參考,只要傳入第二個參數的依賴項保持不變。與 useMemo類似,函數的類型是從第一個參數中函數的回傳值推斷出來的,你可以透過向 Hook 提供類型參數來更明確地指定。
在 TypeScript 嚴格模式下使用useCallback 時,需要為回呼函數中的參數添加類型。這是因為回呼的類型是從函數的回傳值推斷出來的,沒有參數就無法完全理解類型。
根據你的程式碼風格偏好,你可以使用 React 類型中的*EventHandler 函數,在定義回呼的同時為事件處理器提供類型:
實用的型別
來自 @types/react套件的型別相當廣泛,當你對 React 和 TypeScript 的互動方式感到熟悉時,值得一讀。你可以在DefinitelyTyped 中的 React 資料夾找到它們。我們將在這裡介紹一些較常見的型別。
DOM 事件
在 React 中處理 DOM 事件時,事件的型別通常可以從事件處理器推斷出來。然而,當你想提取一個函數並傳遞給事件處理器時,你需要明確設定事件的型別。
React 型別中提供了許多事件型別——完整清單可以在這裡找到,該清單基於DOM 中最常見的事件。
在確定你要尋找的型別時,你可以先查看你正在使用的事件處理器的懸停資訊,這將顯示事件的型別。
如果你需要使用未包含在此清單中的事件,可以使用 React.SyntheticEvent型別,這是所有事件的基礎型別。
子元素
描述元件的子元素有兩種常見途徑。第一種是使用 React.ReactNode型別,它是所有可以作為 JSX 子元素傳遞的可能型別的聯集:
這是對子元素非常寬泛的定義。第二種是使用 React.ReactElement型別,它僅指 JSX 元素,而不包括像字串或數字這樣的 JavaScript 原始型別:
請注意,你無法使用 TypeScript 來描述子元素是特定型別的 JSX 元素,因此你無法使用型別系統來描述一個只接受<li>子元素的元件。
你可以在 React.ReactNode 和 React.ReactElement與型別檢查器的範例。這個 TypeScript 遊樂場中看到
樣式屬性
在 React 中使用行內樣式時,你可以使用React.CSSProperties 來描述傳遞給 style屬性的物件。此型別是所有可能 CSS 屬性的聯集,是確保你將有效的 CSS 屬性傳遞給style屬性,並在編輯器中獲得自動完成的好方法。
延伸學習
本指南涵蓋了在 React 中使用 TypeScript 的基礎知識,但還有更多內容需要學習。 文件中的個別 API 頁面可能包含關於如何與 TypeScript 一起使用的更深入文件。
我們推薦以下資源:
- TypeScript 手冊是 TypeScript 的官方文件,涵蓋了大多數關鍵的語言特性。
- TypeScript 版本發布說明深入介紹了新功能。
- React TypeScript 速查表是一個社群維護的速查表,用於在 React 中使用 TypeScript,涵蓋了許多有用的邊緣案例,並提供了比本文檔更廣泛的內容。
- TypeScript 社群 Discord是詢問問題和獲得 TypeScript 與 React 問題幫助的好地方。
