Borislav Hadzhiev
Wed Apr 06 2022·3 min read
Photo by Anh Q Tran
The React.js error "Property 'X' does not exist on type 'HTMLElement'" occurs
when we try to access a property that doesn't exist on an element of type
HTMLElement
. To solve the error, use a type assertion to type the element
correctly before accessing the property.
And here are 3 examples of how the error occurs.
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="https://google.com" target="_blank" rel="noreferrer"> Open Google </a> <button id="btn">Submit</button> </div> ); }
The reason we got the error is because the return type of the
document.getElementById
method is HTMLElement | null
and the properties we tried to access don't exist
on the HTMLElement
type.
To solve the error, use a type assertion to type the element correctly, e.g. as
HTMLInputElement
, HTMLButtonElement
, HTMLAnchorElement
,
HTMLImageElement
, HTMLDivElement
, HTMLTextAreaElement
, etc - depending on
the type of element you are working with.
The types are consistently named as HTML***Element
. Once you start typing
HTML..
, your IDE should be able to help you with autocomplete.
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="https://google.com" target="_blank" rel="noreferrer"> Open Google </a> <button id="btn">Submit</button> </div> ); }
Type assertions are used when we have information about the type of a value that TypeScript can't know about.
input
variable stores anHTMLInputElement
and not to worry about it.Similarly, we typed the link
variable to be an HTMLAnchorElement
and the
btn
variable - HTMLButtonElement
.
You can also use a type assertion inline, right before accessing a property.
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="https://google.com" target="_blank" rel="noreferrer"> Open Google </a> <button id="btn">Submit</button> </div> ); }
An inline type assertion gets the job done if you only need to access a property once and don't want to assign the element to a variable.
If you want to be more precise with the type of the elements, you can use a
union
to set the type as 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="https://google.com" target="_blank" rel="noreferrer"> Open Google </a> <button id="btn">Submit</button> </div> ); }
The HTML***Element
or null
type is the correct type, because if an element
with the provided id
does not exist in the DOM, the
document.getElementById()
method would return a null
value.
You can use the
optional chaining (?.)
operator to short-circuit if the reference is nullish (null
or undefined
)
right before accessing the property.
Alternatively, you can use a simple if
statement that serves as a
type guard,
like we did with the button
element.
It's always a best practice to include null
in the type assertion because the
getElementById
method would return null
if no element with the provided id
was found.