在上一篇文章[TypeScript] 將TypeScript 導入到React專案- 起手式 ,記錄在React 專案中如何設定TypeScript開發環境、簡單介紹TypeScrip以及導入TypeScript的原因
接下來是將導入TypeScript 導入React 專案後遇到的問題
將rootElement 當作createRoot參數時發生錯誤
原因是, 在還沒將TypeScript 加入React App 時, 如果是透過CRA 建立的React App, 預設的src/index.js
內容大概是
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
而其中的getElementById
預設回傳的類別為 HTMLElement
或 null
,
而ReactDOM.createRoot
接收的第一個參數類型是Element
或是 DocumentFragment
其中getElementById
所回傳的類型中, null
, 不是ReactDOM.createRoot
可以接受的類型
以至於導致下方的錯誤
類型 'HTMLElement | null' 的引數不可指派給類型 'Element | DocumentFragment' 的參數。
類型 'null' 不可指派給類型 'Element | DocumentFragment'。
要解決的方式有兩種.
- 型別斷言(Type Assertion)
- Null型別的處理
型別斷言(Type Assertion)
型別斷言(Type Assertion)是為了當TypeScript 無法推斷變數的型別時, 透過型別斷言, 直接指定變數的型別
若改寫上方的例子, 則可以變成
const rootElement = document.getElementById('root') as HTMLElement
const root = ReactDOM.createRoot(rootElement);
透過as
來指定rootElement
的型別就是HTMLElement
這樣當rootElement
傳入ReactDOM.createRoot
時, TypeScript只會將rootElement
判斷成HTMLElement
, 也就不會報錯
Null型別的處理
其實就是跟我們一般在寫JavaScript 時, 通常為了避免在賦值給變數時, 我們以為有正確拿到值, 但實際上是null, 而導致意外使用null呼叫物件或其他型別的屬性.
一般這種狀況我們通常會用if(xxx === null)
來先做預處理
而這個問題也可以用這樣方式處理, 例如
const rootElement = document.getElementById('root')
if(!rootElement) {
throw new Error('Failed to find the root element');
}const root = ReactDOM.createRoot(rootElement);
當加入rootElement
為null
時要如何處理邏輯後, TypeScript 就會將rootElement
的型別視為HTMLElement
(因為rootElement
只會有null
或HTMLElement
兩種可能的型別)
這樣ReactDOM.createRoot
也理所當然不會有任何問題