0

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

4
  • You can write myNumber: this.formBuilder.control<number>(NaN, Validators.required). Then you don't have to check for the number type. Commented Aug 25, 2023 at 13:57
  • I guess that’s right, but let‘s assume that it is not about a number but rather about an object named address. What would you do then? Commented Aug 25, 2023 at 22:07
  • 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). Commented Aug 26, 2023 at 8:07
  • I know, of course, but in this case one would use a custom ControlValueAccessor. Commented Aug 26, 2023 at 19:41

1 Answer 1

0

The null value is a value apart for an empty formControl. For instance, an empty input fields can be initialized with null, meaning there is no content. In this way, you can distinguish null from an empty string. For this reason, by default, any formControl can be null and therefore its type is explicitly as follows:

const nullableFormControl: FormControl<string | null> = new FormControl<string>("Foo");

If you however pass nonNullable : true in the second parameter option, no extra null type will be added to your formControl's type. Here is the equivalent typing:

const onlyStringFormControl: FormControl<string>  = new FormControl<string>("Bar", {nonNullable: true});

In this case, onlyStringFormControl.value will always be a string (no need to type-guard anything)

Note: If you intend to explicitly use and init using null, you need to add null into your formControls's generic type union.

const defaultNullFormControl: FormControl<string | null> = new FormControl<string | null>(null);
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.