'How to work with form elements in typescript
I'd like to access form elements via myForm.elements, and then access each element by it's name, for example, myForm.elements.month. Typescript doesn't like this b/c it doesn't know that form.elements contains a property of month. I thought, let's create an interface! So I did, (see code below), but I'm getting this typescript error: Neither type 'HTMLCollection' nor type 'FormElements' is assignable to the other
Here's the code I'm working with:
interface FormElements {
day: HTMLInputElement;
month: HTMLInputElement;
year: HTMLInputElement;
}
class BirthdateInput {
constructor(form: HTMLFormElement) {
var elements: FormElements = <FormElements> form.elements; // error here
this.day = elements.day;
this.month = elements.month;
this.year = elements.year;
}
}
Any ideas on how to better cast my form.elements object so typescript won't complain?
Solution 1:[1]
Best way would be to write it like this:
// Note 'extends' clause here
interface FormElements extends HTMLFormElement {
day: HTMLInputElement;
month: HTMLInputElement;
year: HTMLInputElement;
}
class BirthdateInput {
constructor(form: HTMLFormElement) {
var elements: FormElements = <FormElements> form.elements; // OK
// ...
Solution 2:[2]
Turns out adding an extends clause fixes it:
interface FormElements extends HTMLCollection {
day: HTMLInputElement;
month: HTMLInputElement;
year: HTMLInputElement;
}
Solution 3:[3]
Origin answer
Create an interface extending HTMLFormControlsCollection or HTMLCollection and add there your inputs
interface FormElements extends HTMLFormControlsCollection {
day: HTMLInputElement
month: HTMLInputElement
year: HTMLInputElement
}
Eventually, for example, getting this
interface FormElements extends HTMLFormControlsCollection {
day: HTMLInputElement
month: HTMLInputElement
year: HTMLInputElement
}
function onSearch(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
const elements = event.currentTarget.elements as FormElements
// ...
}
However
You can create a helper so you don't need to write an interface for each form every time
type FormElements<U extends string> = HTMLFormControlsCollection & Record<U, HTMLInputElement>
And use it like this
const elements = event.currentTarget.elements as FormElements<"id" | "name" | "type" | "amount">
Eventually, for example, getting this
function onSearch(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
const elements = event.currentTarget.elements as FormElements<"id" | "name" | "type" | "amount">
// ...
}
Solution 4:[4]
Whether right or wrong, I have found that you can also do something like this:
interface FormElements {
day: HTMLInputElement;
month: HTMLInputElement;
year: HTMLInputElement;
}
class BirthdateInput {
constructor(form: HTMLFormElement) {
var elements: FormElements = <FormElements>(<any> form.elements);
this.day = elements.day;
this.month = elements.month;
this.year = elements.year;
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Ryan Cavanaugh |
| Solution 2 | lizlux |
| Solution 3 | FrameMuse |
| Solution 4 | Alex Dresko |
