"Property value does not exist": how to make TypeScript and DOM work together
\n\nHave you ever written a simple form handler in TypeScript, started compilation — and got a red line in the console: "The property 'value' does not exist on value of type 'HTMLElement'"? If so — you are not alone. This question has gathered thousands of views on Stack Overflow, and today we will break it down once and for all.
\n\nThe error occurs when you try to get .value from an element that TypeScript considers to be just HTMLElement. The problem is that HTMLElement is a base type, while the value property only exists on HTMLInputElement, HTMLTextAreaElement, and similar descendants. TypeScript is strict: it will not allow you to access a property that (from its perspective) does not exist.
But this is not a bug — it's a feature. TypeScript protects you from runtime errors. Let's figure out how to correctly specify the type and at the same time improve your code's performance.
\n\nWhy does TypeScript complain?
\n\nImagine: you write code for an input field:
\n\nconst input = document.getElementById('myInput');\nconsole.log(input.value); // ❌ Error!\n\nThe getElementById method returns the type HTMLElement | null. HTMLElement does not have a value property. It has innerHTML, className, style — but not value. TypeScript honestly says: "I'm not sure this element has a value."
But you are sure it's an <input>. How do you explain this to the compiler?
Method 1: Type Assertion
\n\nThe most popular and fastest way is to use as:
const input = document.getElementById('myInput') as HTMLInputElement;\nconsole.log(input.value); // ✅ Works\n\nNow TypeScript knows: this is HTMLInputElement, which has value. Simple and effective. But there is a nuance: if the element is not actually an <input> (for example, it's a <div>), you will get undefined at runtime. TypeScript won't help you — it trusts your assertions.
Method 2: Type checking with instanceof
\n\nA safer approach is runtime checking:
\n\nconst element = document.getElementById('myInput');\nif (element instanceof HTMLInputElement) {\n console.log(element.value); // ✅ TypeScript narrows the type automatically\n} else {\n console.error('Element is not an input');\n}\n\nThis method not only solves the type problem but also makes the code more reliable. You explicitly check that the element is exactly what you expect. For educational purposes, I recommend this method: it teaches you to think about types.
\n\nMethod 3: Generic Functions
\n\nFor advanced users — creating a wrapper with generics:
\n\nfunction getInputElement<T extends HTMLElement>(id: string): T | null {\n const element = document.getElementById(id);\n if (element instanceof T) { // ❌ This is not allowed, T is a type, not a value\n return element;\n }\n return null;\n}\n\nUnfortunately, in TypeScript you cannot check instanceof with a generic directly. But you can use function overloads or factories. For simple projects, the first two methods are sufficient.
Performance optimization: why type matters
\n\nYou might ask: "What do types have to do with speed?" Directly. When TypeScript knows the exact type of an element, it generates more efficient JavaScript code. Consider an example:
\n\n// Bad: TypeScript doesn't know the type\nfunction getValue(id: string): string {\n const el = document.getElementById(id);\n // On every call — checking and casting\n return (el as HTMLInputElement).value || '';\n}\n\n// Go