Currently I'm looping over the elements of a form using the following code, but I'm wondering how I could eliminate the repeated casts as HTMLFormElement
. If I exclude them I get a warning that the Property 'type' does not exist on type 'Element'
.
Array.from((e.target as HTMLFormElement).elements)
.filter(el => (el as HTMLFormElement).type === 'radio')
.filter(el => (el as HTMLFormElement).checked === true)
While I can hoist up the cast to an Array
, such as:
const els = Array.from((e.target as HTMLFormElement).elements) as Array<HTMLFormElement>
els
.filter(el => el.type === 'radio')
.filter(el => el.checked === true)
It seems seems like I should be able to simply define/assert the type rather than needing to cast it.
Is there a reason this doesn't work equally as well?
const els: Array<HTMLFormElement> = Array.from((e.target as HTMLFormElement).elements
Full context is a React component.
export const Form = ({ children }: FormProps) => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
e.preventDefault()
const els = Array.from((e.target as HTMLFormElement).elements)
els.filter(el => el instanceof HTMLInputElement && el.type === 'radio' && el.checked === true)
}
return (
<form onSubmit={handleSubmit}>
{children}
<br />
<button type="submit">Submit</button>
</form>
)
}
CodePudding user response:
The repeated assertions are superfluous because you only need a single .filter
callback. Also, the .elements
collection will be composed of control elements, like inputs and buttons - not HTMLFormElement
(which only the containing <form>
is) - two of your as
are asserting inaccurately.
You can get rid of the assertions entirely by testing in the JavaScript whether the element being iterated over is an <input>
(which is safer).
Array.from((e.target as HTMLFormElement).elements)
.filter(el => el instanceof HTMLInputElement && el.type === 'radio' && el.checked === true);
Is there a reason this doesn't work equally as well?
Well, it would need to be typed properly, to start with - but it'd be easier to let TypeScript infer the type automatically (a generic Element
type, as it does now, is fine) and not bother trying to correct it.
Since it sounds like this is in an event handler, if the element the listener is attached to is typed properly, you could refer to it again instead of having to cast e.target
.
const form = document.querySelector('form')!;
form.addEventListener('submit', () => {
Array.from(form.elements)
.filter(el => el instanceof HTMLInputElement && el.type === 'radio' && el.checked === true);
});
though if there's more than one form on the page, you'll need a generic
const form = document.querySelector<HTMLFormElement>('form')!;
If the resulting filtered array needs to be typed, the only option is to be somewhat repetitive by adding as Array<HTMLInputElement>
to the end, or by making .filter
generic.