Type Form Elements in TypeScript
Learn how to type HTML forms, form fields, and named element access in TypeScript so you can collect and validate user input safely.
Forms group input elements together so you can read and validate user data as a set. TypeScript types form elements with dedicated interfaces: the form itself is HTMLFormElement, text inputs are HTMLInputElement, selects are HTMLSelectElement, and textareas are HTMLTextAreaElement. This section covers how to type forms, access named fields, work with selects and checkboxes, and collect all form data safely.
Getting a typed form element
Use querySelector with a generic to get a form with the correct type. HTMLFormElement gives you access to form-specific methods like reset and requestSubmit, plus the elements collection for accessing child fields.
const form = document.querySelector<HTMLFormElement>('#signup');
if (form === null) throw new Error('Form not found');The HTMLFormElement interface provides these key members for working with forms programmatically:
| Member | Type | Purpose |
|---|---|---|
| .elements | HTMLFormControlsCollection | All form controls by name and position |
| .reset() | function | Resets all fields to defaults |
| .requestSubmit() | function | Submits with submit event fired |
| .length | number | Number of form controls |
Accessing named form fields
The standard DOM way to reach a form field is through form.elements.namedItem. However, its return type is broad because TypeScript cannot know whether a given name refers to an input, a select, or a radio group.
const field = form.elements.namedItem('email');
// ^? const field: Element | RadioNodeList | null
if (field instanceof HTMLInputElement) {
console.log(field.value); // OK
console.log(field.type); // 'text', 'email', etc.
}The instanceof check tells TypeScript that field is an HTMLInputElement inside the block. The compiler then allows .value and .type.
For a more direct approach that avoids instanceof, use querySelector scoped to the form element. This gives you the precise type immediately:
const emailInput = form.querySelector<HTMLInputElement>('#email');
if (emailInput !== null) {
console.log(emailInput.value);
}Working with select elements
A select element has its own type, HTMLSelectElement, with properties that do not exist on a plain input. The most commonly used are value for the selected option, selectedIndex for its position, and options for the full list.
const roleSelect = form.querySelector<HTMLSelectElement>('#role');
if (roleSelect === null) throw new Error('Select not found');
console.log(roleSelect.value); // selected value
console.log(roleSelect.selectedIndex); // option index
console.log(roleSelect.options.length); // total optionsA single select only needs .value, but a multi-select can have more than one option chosen at once. Use .selectedOptions, which returns a live collection of HTMLOptionElement, and loop through it to collect every selected value:
const multiSelect = form.querySelector<HTMLSelectElement>('#tags');
if (multiSelect === null) throw new Error('Select not found');
const selectedValues: string[] = [];
for (const option of multiSelect.selectedOptions) {
selectedValues.push(option.value);
}Each option in the loop is automatically typed as HTMLOptionElement, giving you .value and .text.
Checkboxes and radio buttons
A checkbox input has the same HTMLInputElement type as a text input, but you use .checked instead of .value to read its state. The .value property on a checkbox is the string sent when the form submits, not whether the box is ticked.
const agreeCheckbox = form.querySelector<HTMLInputElement>('#agree');
if (agreeCheckbox === null) throw new Error('Checkbox not found');
console.log(agreeCheckbox.checked); // booleanFor a group of radio buttons with the same name, form.elements.namedItem returns RadioNodeList instead of a single element. RadioNodeList has a .value property that gives the value of the selected radio button:
const plan = form.elements.namedItem('plan');
// ^? const plan: Element | RadioNodeList | null
if (plan instanceof RadioNodeList) {
console.log(plan.value); // value of selected radio
}Collecting all form data with FormData
FormData collects every named field's value as a string. It is useful when you want all fields at once rather than reading each one individually.
form.addEventListener('submit', (event) => {
event.preventDefault();
const data = new FormData(form);
const email = data.get('email');
// ^? const email: FormDataEntryValue | null
if (typeof email === 'string') {
console.log(email.toUpperCase()); // OK
}
});FormData.get returns FormDataEntryValue or null, which is string or File or null. If your form has no file inputs, you know the value is a string, but TypeScript still requires the narrowing because the type definition cannot know your specific form's fields. For a more structured approach, read each field individually with its correct type instead of using FormData.
Common mistakes
Accessing .value on a form element instead of a field is a frequent error. HTMLFormElement does not have .value. You need to select a specific field first and then read its value.
Assuming form.elements bracket access returns an input is also common. The bracket access on HTMLFormControlsCollection returns Element or RadioNodeList, which does not have .value. Always narrow the result with instanceof or use querySelector instead.
Not checking for null on a field that may not exist is the third common mistake. Every selector can return null, and you must guard before accessing properties.
See Type Input Values in TypeScript for a deeper look at input-specific properties. See Type Event Handlers in TypeScript for event type details.
Rune AI
Key Insights
- HTMLFormElement has .elements, .reset, and .requestSubmit for working with forms.
- form.elements.namedItem returns Element or RadioNodeList or null, so narrow with instanceof.
- HTMLSelectElement gives .value, .selectedIndex, and .options for dropdowns.
- Use FormData to collect all form values as strings.
- Access specific fields with a scoped querySelector call for the most precise type.
Frequently Asked Questions
How do I access named form fields with the correct type?
How do I type a select element in TypeScript?
How do I collect all form data with proper types?
Conclusion
TypeScript gives you HTMLFormElement, HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement, and HTMLButtonElement to work with form fields. Use querySelector with generics for direct typed access. Always narrow form.elements results with instanceof.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.