Let's assume having a simple form like this:
return this.formBuilder.group({
myNumber: this.formBuilder.control<number | null>(
null,
Validators.required
)
});
The control myNumber
is typed as number | null
as no default value should be shown within the UI. The component should render an empty input
of type number
.
Let's assume that the corresponding form can be submitted and that the submission is handled like this:
protected onSubmit(): void {
if (this.form.invalid) return;
const formValue = this.form.getRawValue();
// Further logic using the valid form value
}
The function onSubmit
does an early return having an invalid form. If the form is valid further logic is executed; e.g. saving/persisting the entered number.
The type of formValue.myNumber
is still number | null
, even though it can't be null
due to the usage of Validators.required
and the check using this.form.invalid
.
This means that an additional check has to be done if the entered number should be provided as a payload for some kind of service.
This means that we have to do something like this:
if (typeof formValue.myNumber == "number") {
// Use formatValue.myNumber as a payload
}
And this is annoying. Why should the same check be done that the validator ensures anyway?
How do you guy's deal with this problem?
In my opinion it would be cool if a validator narrows down the type of the control. E.g. that Validators.required
acts as a type guard and excludes null
as a possible type having a valid form. But as far as I know this is not possible right now, or am I wrong?
I would be really interested how the Angular community deals with this scenario in general. Any thought appreciated.
Stackblitz: https://stackblitz.com/edit/stackblitz-starters-wqjd4q?file=src%2Fmain.ts
Greets Lukas
myNumber: this.formBuilder.control<number>(NaN, Validators.required)
. Then you don't have to check for the number type.HTMLInputElement
doesn't support typing in objects. So in that case, you build form groups and use individual fields for individual controls (i.e. Address Line 1, Address Line 2, City, State, Zip - total 5 input fields).ControlValueAccessor
.