Question
In an Angular 4 application, an input event handler receives an Event. Why does TypeScript report that Property 'value' does not exist on type 'EventTarget' when accessing event.target.value, even though the browser console shows that the target has a value?
onUpdatingServerName(event: Event) {
console.log(event);
this.newserverName = event.target.value;
}
<input
type="text"
class="form-control"
(input)="onUpdatingServerName($event)"
>
How can the input's value be accessed safely and correctly?
Short Answer
You will learn why TypeScript types event.target broadly as EventTarget | null, why not every event target has a value property, and how to narrow or assert it as an HTMLInputElement before reading its value.
Concept
Browser events can come from many different objects: inputs, buttons, forms, documents, windows, and custom event targets. TypeScript therefore uses the general EventTarget type for event.target.
EventTarget only describes objects that can receive and dispatch events. It does not promise input-specific properties such as value, checked, or files.
Also, event.target can be null, so its full type is commonly:
EventTarget | null
An <input> element is more specific than EventTarget. It is represented by HTMLInputElement, which does have a value: string property. Before using value, tell TypeScript—or prove to TypeScript—that the target is an HTMLInputElement.
The runtime console and TypeScript are answering different questions:
- The console shows the particular object that happened to trigger this event.
- TypeScript checks every object that is allowed by the declared type.
This protection prevents code from incorrectly reading .value from targets that do not support it, such as a div or document.
Mental Model
Think of EventTarget as a label saying, “this object can receive mail.” An HTMLInputElement is a more specific label saying, “this object can receive mail and has editable text in a value field.”
When TypeScript sees only the general EventTarget label, it cannot assume there is a value field. You must inspect the object or explicitly state that it is an input before opening that field.
Syntax and Examples
Use a type assertion when the handler is only attached to an input element:
onUpdatingServerName(event: Event): void {
const input = event.target as HTMLInputElement;
this.newserverName = input.value;
}
as HTMLInputElement tells TypeScript to treat the target as an input. This is appropriate when your template guarantees that the event comes from this <input>.
For code that may receive events from different sources, use a type guard instead:
onUpdatingServerName(event: Event): void {
const target = event.target;
if (!(target instanceof HTMLInputElement)) {
return;
}
this.newserverName = target.value;
}
After the instanceof HTMLInputElement check, TypeScript narrows target to HTMLInputElement, so .value is safe.
In an Angular template, another concise option is to pass the value instead of the whole event:
<input
type="text"
class="form-control"
(input)="onUpdatingServerName($any($event.target).value)"
>
onUpdatingServerName(value: string): void {
this.newserverName = value;
}
This works, but keeping DOM type handling in TypeScript is often easier to test and maintain.
Step by Step Execution
Consider this guarded handler:
onUpdatingServerName(event: Event): void {
const target = event.target;
if (!(target instanceof HTMLInputElement)) {
return;
}
this.newserverName = target.value;
}
When a user types Apollo into the input:
- The browser updates the input element's current value to
Apollo. - The browser dispatches an
inputevent. - Angular passes that event to
onUpdatingServerName. event.targetrefers to the element where the event started: the input.instanceof HTMLInputElementverifies that the target is truly an input element.- TypeScript now knows
targethas thevalueproperty. target.valuereturns the stringApollo.- The component assigns
Apollotothis.newserverName.
If the handler were accidentally called with an event from a non-input target, it would return before attempting to read .value.
Real World Use Cases
DOM event target narrowing is useful whenever a user action provides data through an HTML element:
- Search fields: read
HTMLInputElement.valueas the user types. - Checkboxes: read
HTMLInputElement.checkedafter a change. - File uploads: read
HTMLInputElement.filesfrom a file input. - Textareas: narrow to
HTMLTextAreaElementand readvalue. - Select menus: narrow to
HTMLSelectElementand read the selectedvalue. - Event delegation: a container handler checks whether
event.targetis a button, link, or input before acting.
Example for a checkbox:
onNewsletterChange(event: Event): void {
const input = event.target as HTMLInputElement;
this.isSubscribed = input.checked;
}
Real Codebase Usage
In production code, prefer patterns that make assumptions clear and keep unsafe assertions small.
Use a guard for reusable handlers
A shared handler may be called from different elements. Validate the target first:
function getInputValue(event: Event): string | undefined {
const { target } = event;
if (!(target instanceof HTMLInputElement)) {
return undefined;
}
return target.value;
}
Use an early return
Early returns avoid deeply nested if statements:
onInput(event: Event): void {
if (!(event.target instanceof HTMLInputElement)) return;
this.newserverName = event.target.value.trim();
}
Pass data rather than browser events
Components are often easier to reuse when a method accepts the data it needs:
onUpdatingServerName(name: string): void {
this.newserverName = name;
}
The template or a small adapter can extract the DOM value.
Validate user input separately
A correct type does not mean valid content. After obtaining a string, apply application rules:
const name = input.value.trim();
if (name.length === 0) {
this.validationMessage = "A server name is required.";
return;
}
Type narrowing checks the shape of the DOM object; validation checks whether the user's data is acceptable.
Common Mistakes
Accessing value directly on EventTarget
This fails because EventTarget does not define value:
// Error: Property 'value' does not exist on type 'EventTarget'.
this.newserverName = event.target.value;
Narrow or assert the target first.
Forgetting that target may be null
Even this is incomplete under strict null checking:
const target: EventTarget | null = event.target;
target.value;
Use an instanceof check, which handles both the element type and null:
if (event.target instanceof HTMLInputElement) {
console.log(event.target.value);
}
Casting to the wrong element type
This compiles but can be wrong at runtime:
const input = event.target as HTMLInputElement;
console.log(input.checked);
If the handler is attached to a <select>, use HTMLSelectElement; for a <textarea>, use HTMLTextAreaElement.
Using any everywhere
const value = (event.target as any).value;
This suppresses useful type checks. It may be acceptable in a narrowly scoped Angular template expression, but prefer a specific DOM type or a runtime guard in TypeScript.
Confusing target and currentTarget
target is the element where the event originated. currentTarget is the element whose listener is currently running. With nested elements and event bubbling, they can differ. Use the one that matches your intent.
Comparisons
| Approach | Best use case | Safety |
|---|---|---|
event.target as HTMLInputElement | The template guarantees the handler is attached to an input | Compile-time assertion only; no runtime check |
event.target instanceof HTMLInputElement | Shared, delegated, or defensive event handlers | Runtime check plus TypeScript narrowing |
Pass value: string to the method | Component logic should not depend on DOM events | Keeps business logic simple and typed |
(event.target as any).value | Temporary compatibility workaround | Avoid when possible; removes type checking |
target and currentTarget also have different meanings:
| Property | Meaning | Common caveat |
|---|---|---|
event.target | The element that originally triggered the event | May be a nested child during bubbling |
event.currentTarget | The element where the current listener is registered | Its DOM type is still not inferred automatically in a generic handler |
Cheat Sheet
// Input value: assertion
const value = (event.target as HTMLInputElement).value;
// Input value: safe runtime guard
if (event.target instanceof HTMLInputElement) {
const value = event.target.value;
}
// Checkbox state
if (event.target instanceof HTMLInputElement) {
const checked = event.target.checked;
}
// Select value
if (event.target instanceof HTMLSelectElement) {
const value = event.target.value;
}
// Textarea value
if (event.target instanceof HTMLTextAreaElement) {
const value = event.target.value;
}
EventTargetdoes not contain input-specific properties.event.targetmay benull.- Use
instanceofwhen the event source is uncertain. - Use
as HTMLInputElementonly when the surrounding code guarantees the target is an input. valueis always a string for text, select, and textarea values; convert it when a number is required.
FAQ
Why does the browser console show value but TypeScript reports an error?
The console shows the actual runtime element. TypeScript sees the broader declared type, EventTarget | null, which could also represent objects without a value property.
What is the correct TypeScript type for a text input?
Use HTMLInputElement. It includes properties such as value, checked, files, and type.
Is event.target as HTMLInputElement safe?
It is safe when you know the event handler is only used by an <input>. If the handler can be reused or invoked from other elements, use instanceof HTMLInputElement instead.
Why does TypeScript say that event.target might be null?
The DOM event type allows a missing target in some situations. A type guard such as event.target instanceof HTMLInputElement handles this automatically.
Should I use target or currentTarget?
Use target to inspect the element that initiated the event. Use currentTarget when you need the element where the listener is attached. They may differ when events bubble through nested elements.
How do I read a numeric input value?
input.value is still a string. Convert it deliberately:
const amount = Number(input.value);
Check for invalid values when needed with Number.isNaN(amount).
How do I get a value from a select element?
Narrow the target to HTMLSelectElement and use its value property.
Mini Project
Description
Build a small Angular server-name field that reads input events safely, keeps the current value in component state, and displays a validation message. This demonstrates DOM type narrowing and separates obtaining user input from validating it.
Goal
Create an input handler that accepts only events from text inputs and stores a non-empty server name.
Requirements
Requirement 1
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.
Angular *ngFor: Fix “Can't Bind to ngFor” Errors
Learn why Angular cannot bind to ngFor, how structural directive microsyntax works, and how to import NgFor or CommonModule correctly.