У меня есть функция, которая принимает элемент формы как входной, фильтрует его дочерние элементы (удаляя их без сериализуемых значений), а затем возвращает объект формы {name: value}
.
Ошибка typechecking, кажется, терпит неудачу, потому что Flow сообщает мне, что каждый элемент, переданный функции фильтра, является всего лишь базовым HTMLElement
. Я фильтрую, проверяя element.type
, element.name
т.д., А поскольку HTMLElement
не имеет этих свойств, возникает ошибка.
Две функции фильтрации используют тип объединения, содержащий соответствующие элементы, которые я ожидаю там (HTMLInputElement
, HTMLTextAreaElement
т.д.) Для typecheck. Само по себе эти работы. Только когда они вызываются в фильтр, я получаю ошибки.
Полный код для модуля:
/* @flow
* Returns form data as a javascript object. Requires each
* form element to have a name that corresponds to its value.
*/
type FormChild =
| HTMLInputElement
| HTMLSelectElement
| HTMLTextAreaElement
| HTMLButtonElement
| HTMLFieldSetElement;
const isValidTarget = (el: FormChild): boolean => {
return !(el instanceof HTMLFieldSetElement) &&
!(el instanceof HTMLButtonElement) &&
el.name !== '';
};
const isSerializable = (el: FormChild): boolean => {
return (el.type === 'radio' && el.checked === true) ||
(el.type === 'checkbox' && el.checked === true) ||
(el.type !== 'radio' && el.type !== 'checkbox');
};
export default (form: HTMLFormElement): { [elementName: string]: string } => {
return [...form.elements].filter((el) => isValidTarget(el) && isSerializable(el))
.reduce((data, el) => ({ [el.name]: el.value, ...data }), {});
};
И ошибки, которые я получаю, относятся к вызову в фильтре:
HTMLElement This type is incompatible with union: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement | HTMLFieldSetElement
и связанные с reduce
:
property 'name' Property not found in HTMLElement
property 'value' Property not found in HTMLElement
Есть два шага:
HTMLElement
в FormChild
,HTMLFieldSetElement
из типа FormChild
поскольку он не может иметь атрибут value
и будет отфильтрован с помощью isValidTarget()
./* @flow
* Returns form data as a javascript object. Requires each
* form element to have a name that corresponds to its value.
*/
type FormChild =
| HTMLInputElement
| HTMLSelectElement
| HTMLTextAreaElement;
//| HTMLButtonElement <— removed, will be filtered by isValidTarget()
//| HTMLFieldSetElement; <— removed, will be filtered by isValidTarget()
const isValidTarget = (el: FormChild): boolean => {
return !(el instanceof HTMLFieldSetElement) &&
!(el instanceof HTMLButtonElement) &&
el.name !== '';
};
const isSerializable = (el: FormChild): boolean => {
return (el.type === 'radio' && el.checked === true) ||
(el.type === 'checkbox' && el.checked === true) ||
(el.type !== 'radio' && el.type !== 'checkbox');
};
export default (form: HTMLFormElement): { [elementName: string]: string } => {
// 'el: any' — prepare to cast
return [...form.elements].filter((el: any) => isValidTarget(el) && isSerializable(el))
.reduce((data, el: FormChild) => ({ [el.name]: el.value, ...data }), {});
// 'el: FormChild' — type was casted
};
Протестировано с потоком: 4.0.5