React報錯之Property ‘X’ does not exist on type ‘HTMLElement’
- 2022 年 7 月 31 日
- 筆記
正文從這開始~
總覽
在React中,當我們試圖訪問類型為HTMLElement
的元素上不存在的屬性時,就會發生Property ‘X’ does not exist on type ‘HTMLElement’錯誤。為了解決該錯誤,在訪問屬性之前,使用類型斷言來正確地類型聲明元素。
這裡有三個例子來展示錯誤是如何發生的。
// App.tsx
import {useEffect} from 'react';
export default function App() {
useEffect(() => {
const input = document.getElementById('first_name');
// ⛔️ Property 'value' does not exist on type 'HTMLElement'.ts(2339)
console.log(input?.value);
// -----------------------------------------------------------------
const link = document.getElementById('link');
// ⛔️ Property 'href' does not exist on type 'HTMLElement'.ts(2339)
console.log(link?.href);
// -----------------------------------------------------------------
const button = document.getElementById('btn');
if (button != null) {
// ⛔️ Property 'disabled' does not exist on type 'HTMLElement'.ts(2339)
button.disabled = true;
}
}, []);
return (
<div>
<input
id="first_name"
type="text"
name="first_name"
defaultValue="Initial Value"
/>
<a id="link" href="<//google.com>" target="_blank" rel="noreferrer">
Open Google
</a>
<button id="btn">Submit</button>
</div>
);
}
產生錯誤的原因是,document.getElementById
方法的返回類型是HTMLElement | null
,但是我們試圖訪問的屬性不存在於HTMLElement
類型。
類型斷言
為了解決這個錯誤,使用類型斷言來為元素正確地進行類型聲明。比如說,類型斷言為HTMLInputElement
, HTMLButtonElement
, HTMLAnchorElement
, HTMLImageElement
, HTMLDivElement
, HTMLTextAreaElement
等等。這取決於你所處理的元素。
這些類型始終命名為HTML***Element
。一旦你開始輸入HTML…
,你的IDE將會幫你自動補全。
import {useEffect} from 'react';
export default function App() {
useEffect(() => {
// ✅ type elements correctly via type assertions
const input = document.getElementById('first_name') as HTMLInputElement;
console.log(input?.value);
const link = document.getElementById('link') as HTMLAnchorElement;
console.log(link?.href);
const button = document.getElementById('btn') as HTMLButtonElement;
if (button != null) {
button.disabled = true;
}
}, []);
return (
<div>
<input
id="first_name"
type="text"
name="first_name"
defaultValue="Initial Value"
/>
<a id="link" href="<//google.com>" target="_blank" rel="noreferrer">
Open Google
</a>
<button id="btn">Submit</button>
</div>
);
}
類型斷言被用於我們知道值的類型資訊,但是TypeScript卻不知道的時候。
我們明確的告訴TypeScript,
input
變數上存儲了HTMLInputElement
,並讓TS不要擔心。
同樣的,我們將link
變數類型聲明為HTMLAnchorElement
,將btn
變數類型聲明為HTMLButtonElement
。
你可以在訪問一個屬性之前,內聯使用類型斷言。
import {useEffect} from 'react';
export default function App() {
useEffect(() => {
const value = (document.getElementById('first_name') as HTMLInputElement).value;
console.log(value);
}, []);
return (
<div>
<input
id="first_name"
type="text"
name="first_name"
defaultValue="Initial Value"
/>
<a id="link" href="<//google.com>" target="_blank" rel="noreferrer">
Open Google
</a>
<button id="btn">Submit</button>
</div>
);
}
如果你只需要訪問屬性一次,並且不希望將元素分配給變數,那麼內聯類型聲明可以完成這項工作。
如果你想更精確地處理元素的類型,可以使用聯合類型將類型聲明為HTML***Element | null
。
import {useEffect} from 'react';
export default function App() {
useEffect(() => {
const input = document.getElementById(
'first_name',
) as HTMLInputElement | null;
console.log(input?.value);
const link = document.getElementById('link') as HTMLAnchorElement | null;
console.log(link?.href);
const button = document.getElementById('btn') as HTMLButtonElement | null;
if (button != null) {
button.disabled = true;
}
}, []);
return (
<div>
<input
id="first_name"
type="text"
name="first_name"
defaultValue="Initial Value"
/>
<a id="link" href="<//google.com>" target="_blank" rel="noreferrer">
Open Google
</a>
<button id="btn">Submit</button>
</div>
);
}
HTML***Element
或者null
類型是最準確的類型,因為如果DOM元素上不存在id
屬性,那麼document.getElementById()
將會返回null
。
你可以使用可選鏈操作符(?.
)在訪問屬性之前來進行短路運算,如果引用是空值(null
或者undefined
)的話。
或者,你可以使用簡單的if
語句作為類型守衛,就像我們對button
處理的那樣。
總結
最佳實踐是在類型斷言中包含null
。因為如果元素上面不提供id
屬性,那麼getElementById
方法將會返回null
。