Borislav Hadzhiev
Sun Mar 27 2022·3 min read
Photo by Photo Nic
The error "Property 'X' does not exist on type 'Element'" occurs when we try
to access a property that doesn't exist on an element of type Element
. To
solve the error, use a type assertion to type the element correctly before
accessing the property.
This is the index.html
file for the examples in this article.
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> </head> <body> <input id="first_name" type="text" name="first_name" value="Initial Value" /> <a id="link" href="https://google.com" target="_blank">Open Google</a> <button id="btn">Submit</button> <script src="./src/index.ts"></script> </body> </html>
And here are 3 examples of how the error occurs in the index.ts
file.
const input = document.querySelector('#input'); if (input != null) { // ⛔️ Property 'value' does not exist on type 'Element'.ts(2339) const value = input.value; console.log(value); } // --------------------------- const link = document.querySelector('#link'); if (link != null) { // ⛔️ Property 'href' does not exist on type 'Element'.ts(2339) const href = link.href; console.log(href); } // --------------------------- const btn = document.querySelector('#btn'); if (btn != null) { // ⛔️ Property 'disabled' does not exist on type 'Element'.ts(2339) btn.disabled = true; }
The reason we got the error is because the return type of the
document.querySelector
method is Element | null
and the properties we tried to access don't exist on
the Element
type.
To solve the error, use a type assertion to type the element correctly, e.g. as
HTMLInputElement
, HTMLButtonElement
, HTMLAnchorElement
, HTMLImageElement
, 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.
const input = document.querySelector('#input') as HTMLInputElement | null; if (input != null) { const value = input.value; console.log(value); } // --------------------------- const link = document.querySelector('#link') as HTMLAnchorElement | null; if (link != null) { const href = link.href; console.log(href); } // --------------------------- const btn = document.querySelector('#btn') as HTMLButtonElement | null; if (btn != null) { btn.disabled = true; }
If you used the
document.getElementsByClassName
method, type the collection as HTMLCollectionOf<YourElementType>
.
// 👇️ with getElementsByClassName // type as HTMLCollectionOf<HTMLInputElement> const inputs = document.getElementsByClassName( 'input', ) as HTMLCollectionOf<HTMLInputElement>; for (let i = 0; i < inputs.length; i++) { const value = inputs[i].value; console.log(value); }
Type assertions are used when we have information about the type of a value that TypeScript can't know about.
input
variable stores anHTMLInputElement
or a null
value and not to worry about it.Similarly, we typed the link
variable to be an HTMLAnchorElement
or null
and the btn
variable - HTMLButtonElement
or null
.
We used a
union type
to specify that the variable could still be null
, because if an HTML element
with the provided selector does not exist in the DOM, the querySelector()
method returns a null
value.
We used a simple if
statement that serves as a
type guard
to make sure the input
variable doesn't store a null
value before accessing
its value
property.
const input = document.querySelector('#input') as HTMLInputElement | null; // 👉️ input has type HTMLInputElement or null here if (input != null) { // 👉️ input has type HTMLInputElement here const value = input.value; console.log(value); }
input
variable has a type of HTMLInputElement
in the if
block and allows us to directly access its value
property.It's always a best practice to include null
in the type assertion, because the
querySelector
method would return null
if no element with the provided
select was found.