Reputation: 636
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?
Upvotes: 12
Views: 28310
Reputation: 373
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
// ...
}
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">
// ...
}
Upvotes: 6
Reputation: 5203
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;
}
}
Upvotes: 0
Reputation: 636
Turns out adding an extends
clause fixes it:
interface FormElements extends HTMLCollection {
day: HTMLInputElement;
month: HTMLInputElement;
year: HTMLInputElement;
}
Upvotes: 5
Reputation: 221024
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
// ...
Upvotes: 6