The property 'value' does not exist on value of type 'HTMLElement'

Typescript

Typescript Problem Overview


I am playing around with typescript and am trying to create a script that will update a p-element as text is inputted in a input box.

The html looks as following:

<html>
    <head>
    </head>
    <body>
        <p id="greet"></p>
        <form>
            <input id="name" type="text" name="name" value="" onkeyup="greet('name')" />
        </form>
    </body>
    <script src="greeter.js"></script>
</html>

And the greeter.ts file:

function greeter(person)
{
	return "Hello, " + person;
}

function greet(elementId)
{
	var inputValue = document.getElementById(elementId).value;

	if (inputValue.trim() == "")
		inputValue = "World";

	document.getElementById("greet").innerText = greeter(inputValue);
}

When I compile with tsc I get the following "error":

/home/bjarkef/sandbox/greeter.ts(8,53): The property 'value' does not exist on value of type 'HTMLElement'

However the compiler does output a javascript file, which works just fine in chrome.

How come I get this error? And how can I fix it?

Also, where can I look up which properties are valid on a 'HTMLElement' according to typescript?

Please note I am very new to javascript and typescript, so I might be missing something obvious. :)

Typescript Solutions


Solution 1 - Typescript

Based on Tomasz Nurkiewiczs answer, the "problem" is that typescript is typesafe. :) So the document.getElementById() returns the type HTMLElement which does not contain a value property. The subtype HTMLInputElement does however contain the value property.

So a solution is to cast the result of getElementById() to HTMLInputElement like this:

var inputValue = (<HTMLInputElement>document.getElementById(elementId)).value;

<> is the casting operator in typescript. See the question TypeScript: casting HTMLElement.

If you're in a .tsx file the casting syntax above will throw an error. You'll want to use this syntax instead:

(document.getElementById(elementId) as HTMLInputElement).value

The resulting javascript from the line above looks like this:

inputValue = (document.getElementById(elementId)).value;

i.e. containing no type information.

Solution 2 - Typescript

If you are using react you can use the as operator.

let inputValue = (document.getElementById(elementId) as HTMLInputElement).value;

Solution 3 - Typescript

We could assert

const inputElement: HTMLInputElement = document.getElementById('greet')

Or with as-syntax

const inputElement = document.getElementById('greet') as HTMLInputElement

Giving

const inputValue = inputElement.value // now inferred to be string

Solution 4 - Typescript

Try casting the element you want to update to HTMLInputElement. As stated in the other answers you need to hint to the compiler that this is a specific type of HTMLElement:

var inputElement = <HTMLInputElement>document.getElementById('greet');
inputElement.value = greeter(inputValue);

Solution 5 - Typescript

A quick fix for this is use [ ] to select the attribute.

function greet(elementId) {
    var inputValue = document.getElementById(elementId)["value"];
    if(inputValue.trim() == "") {
        inputValue = "World";
    }
    document.getElementById("greet").innerText = greeter(inputValue);
}

I just try few methods and find out this solution,
I don't know what's the problem behind your original script.

For reference you may refer to Tomasz Nurkiewicz's post.

Solution 6 - Typescript

The problem is here:

document.getElementById(elementId).value

You know that HTMLElement returned from getElementById() is actually an instance of HTMLInputElement inheriting from it because you are passing an ID of input element. Similarly in statically typed Java this won't compile:

public Object foo() {
  return 42;
}

foo().signum();

signum() is a method of Integer, but the compiler only knows the static type of foo(), which is Object. And Object doesn't have a signum() method.

But the compiler can't know that, it can only base on static types, not dynamic behaviour of your code. And as far as the compiler knows, the type of document.getElementById(elementId) expression does not have value property. Only input elements have value.

For a reference check HTMLElement and HTMLInputElement in MDN. I guess Typescript is more or less consistent with these.

Solution 7 - Typescript

Also for anyone using properties such as Props or Refs without your "DocgetId's" then you can:

("" as HTMLInputElement).value;

Where the inverted quotes is your props value so an example would be like so:

var val = (this.refs.newText as HTMLInputElement).value;
alert("Saving this:" + val);

Solution 8 - Typescript

For those who might still be struggling with this, another alternative is using (document.getElementById(elementId) as HTMLInputElement).value = ''. source.

Should in case you still face issues with it then try extracting it to a function like:

function myInput() {
   (document.getElementById(elementId) as HTMLInputElement).value = ''
}

Solution 9 - Typescript

There is a way to achieve this without type assertion, by using generics instead, which are generally a bit nicer and safer to use.

Unfortunately, getElementById is not generic, but querySelector is:

const inputValue = document.querySelector<HTMLInputElement>('#greet')!.value;

Similarly, you can use querySelectorAll to select multiple elements and use generics so TS can understand that all selected elements are of a particular type:

const inputs = document.querySelectorAll<HTMLInputElement>('.my-input');

This will produce a NodeListOf<HTMLInputElement>.

Solution 10 - Typescript

If you are using angular you can use -

const element = document.getElementById('elemId') as HTMLInputElement;

Solution 11 - Typescript

const a = document.getElementById("a")
if (a instanceof HTMLInputElement) {
	// a.value is valid here
	console.log(a.value)
}

A Safer Way

The above code snippet is the gist of the anwser; continue reading for the reasoning.

Most existing answers recommended Type assertions (type casts) which do the job but are a bit like using any—which disables type checking. There is a better, safer way.

Type assertions are like telling TypeScript to pretend that a variable is of the type we say it is. As such, TypeScript will perform type checking for that type. If we've made a mistake and told it the wrong type, we will get a false sense of security as there will be no compilation warnings, but there will be errors at runtime. Let's look at an example:

// <input id="a" value="1">
// <div   id="b" value="2"></div>

const a = document.getElementById("a") as HTMLInputElement
const b = document.getElementById("b") as HTMLInputElement

console.log(a.value) // 1
console.log(b.value) // undefined

We've told TypeScript that a and b are of type HTMLInputElement and it will treat them as such. However, as b is of type HTMLDivElement which doesn't have the value property, b.value returns undefined.

Type Narrowing with Type Guards

A better way is using type guards which allow for greater control.

A type guard is a way of determining a variable's type at runtime. While type assertions just say: "variable x is of type T", type guards say: "variable x is of type T if it has these properties". Let's quickly see how a type guard looks:

const a = document.getElementById("a")
if (a instanceof HTMLInputElement) {
	// a == input element
} else {
	// a != input element
}

This type guard simply checks if a is an instance of HTMLInputElement. If it is, TypeScript will recognize that and treat a inside the if block as being that type—it will allow access to all the properties of the input element. This is called type narrowing.

Why should you use type guards over type assertions? For the same reason you handle errors. While type guards still won't output warnings at compile time, they put you in control. You (and TypeScript) cannot guarantee whether the value property exists, but with type assertions you can decide what to do in the case it doesn't. Type assertions are like ignoring errors and type guards are like handling them.

How to Use Type Guards

We will show three ways of fixing "The property does not exist" error. Each example uses the same HTML but it's included separately in each so it's easier to read.

  1. Type assertions
// <input id="a" value="1">
// <div   id="b" value="2">

const a = document.getElementById("a") as HTMLInputElement // correct type assertion
const b = document.getElementById("b") as HTMLInputElement // incorrect type assertion
const c = document.getElementById("c") as HTMLInputElement // element doesn't exist

console.log(a.value) // 1
console.log(b.value) // undefined
console.log(c.value) // Uncaught TypeError: Cannot read property 'value' of null
  1. Inline type guards
// <input id="a" value="1">
// <div   id="b" value="2">

const a = document.getElementById("a")
const b = document.getElementById("b")
const c = document.getElementById("c")

if (a instanceof HTMLInputElement) {
	console.log(a.value) // 1
}
if (b instanceof HTMLInputElement) {
	console.log(b.value)
}
if (c instanceof HTMLInputElement) {
	console.log(c.value)
}

b and c didn't log anything as they are not input elements. Notice that we didn't get any unexpected behavior as in the "type assertions" examples.

Note that this is a contrived example, usually you would handle the cases when types are not the ones you expected. I often throw if a type doesn't match, like so:

if (!(b instanceof HTMLInputElement)) {
	throw new Error("b is not an input element")
}
// b == input element (for the rest of this block)
  1. Function type guards

This example is a little more advanced and a little unnecessary for this use case. However, it shows off function type guards and a more "flexible" type guard.

Function type guards are functions that determine whether the given value is of a certain type. They do so by simply returing a bool. For TypeScript to be able to understand that you are making a type guard, you must use a type predicate (see coment in example below).

// <input id="a" value="1">
// <div   id="b" value="2">

const a = document.getElementById("a")
const b = document.getElementById("b")
const c = document.getElementById("c")

if (hasValueProperty(a)) {
	console.log(a.value) // 1
}
if (hasValueProperty(b)) {
	console.log(b.value)
}
if (hasValueProperty(c)) {
	console.log(c.value)
}

const d = {
	"value": "d",
}
if (hasValueProperty(d)) {
	console.log(d.value) // d
}

type WithValue = {
	value: string
}

// hasValueProperty is a type guard function the determines whether x is of type
// WithValue.
//
// "x is WithValue" is a type predicate that tells TypeScript that this is a
// type guard.
function hasValueProperty(x: unknown): x is WithValue {
	return typeof x === "object" && x !== null && typeof (x as WithValue).value === "string"
}

Notice that, since our type guard is only checking for the presence of the "value" property, it can also be used for any other object (not just elements).

Solution 12 - Typescript

This work for me:

let inputValue = (swal.getPopup().querySelector('#inputValue ')as HTMLInputElement).value

Solution 13 - Typescript

I've been having a similar issue (https://stackoverflow.com/questions/69470746/ts-warning-in-js-file-property-x-does-not-exist-on-type-x-is-it-possible-to)

While the tag helped remove the warning in the typescript file, I would still get a warning in my JavaScript file after compiling.

So how do I write code that is clean AND that allows me to manipulate the .value ?

It took me quite some time but I found the solution by using another method:

HTML code:

<form id="my-form" 
   action="index.html"
   method="get"
   onsubmit="return showValue();">
    <input type="text" name="username">
    <input type="text" name="full-name">
    <input type="password" name="password">
    <button type="button" onclick="showValue();">Show value</button>
</form>

Javascript code:

function showValue() {
    const myForm = document.forms.my-form;
    console.log(myForm?.username.value);
    return 1;
}

The document.forms.x has a property "value" and that removes warnings both in the typescript file and in the resulting JavaScript.

Solution 14 - Typescript

If you have dynamic element ID where you need to assign the dynamic value, you may use this:

//element_id = you dynamic id.
//dynamic_val = you dynamic value.
let _el = document.getElementById(element_id);
_el.value = dynamic_val.toString();

This works for me.

Solution 15 - Typescript

This might seem trite, but this is what worked for me:

yarn add -D @types/web

I think I must have had an out of date type definition.

Solution 16 - Typescript

Problem: > error Property 'text' does not exist on type 'HTMLElement'

Solution: in typeScript we need to cast document.getElementById() which returns type HTMLElement in < HTMLScriptElement >

So we can do it by by following way to resolve the error as expected by typescript.js

Code: var content: string = ( < HTMLScriptElement > document.getElementById(contentId)).text;

It worked for me.. hope it works for you as well.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBjarke Freund-HansenView Question on Stackoverflow
Solution 1 - TypescriptBjarke Freund-HansenView Answer on Stackoverflow
Solution 2 - TypescriptMichaelView Answer on Stackoverflow
Solution 3 - TypescriptLeoView Answer on Stackoverflow
Solution 4 - TypescriptwoodysanView Answer on Stackoverflow
Solution 5 - TypescriptinDreamView Answer on Stackoverflow
Solution 6 - TypescriptTomasz NurkiewiczView Answer on Stackoverflow
Solution 7 - TypescriptKidKodeView Answer on Stackoverflow
Solution 8 - TypescriptemmaakachukwuView Answer on Stackoverflow
Solution 9 - TypescriptCertainPerformanceView Answer on Stackoverflow
Solution 10 - TypescriptRohit GroverView Answer on Stackoverflow
Solution 11 - TypescripttouchmarineView Answer on Stackoverflow
Solution 12 - TypescriptJuan PabloView Answer on Stackoverflow
Solution 13 - TypescriptStebenwolfView Answer on Stackoverflow
Solution 14 - TypescriptJaMondoView Answer on Stackoverflow
Solution 15 - TypescriptjfunkView Answer on Stackoverflow
Solution 16 - TypescriptAlok AdhaoView Answer on Stackoverflow