Angular Strictly Typed Forms & NonNullableFormBuilder (Introduced in Angular 14)
April 5, 2025 8:20 PM

In previous Angular versions, most of these APIs included any somewhere in their types, and interacting with the structure of the controls, or the values themselves, was not type-safe. For example: you could write the following invalid code:
const emailDomain = login.value.email.domain;
Ref. Angular Official Doc: https://angular.dev/guide/forms/typed-forms
Angular's strictly typed forms bring type safety to reactive forms, ensuring that the values and structure of forms align with TypeScript types. This feature improves developer productivity and reduces runtime errors.
Strongly Typed Form Controls
- Form controls (
FormControl
,FormGroup
,FormArray
) are now strictly typed, ensuring that the values match the expected types.
- Form controls (
Type Inference
- When using FormBuilder, Angular can infer the types of form controls based on their initial values.
Improved Developer Experience
- Autocompletion and type checking in IDEs help catch errors during development.
Custom Types for Form Groups
- You can define custom interfaces for your form structure to ensure type safety.
Backward Compatibility
- Strictly typed forms are opt-in, so existing forms without types will still work as before.
Benefits of Strictly Typed Forms
- Type Safety: Prevents runtime errors by ensuring form values match their expected types.
- Better Autocompletion: IDEs provide better suggestions for form controls and values.
- Improved Maintainability: Makes it easier to refactor and debug forms in large applications.
- Reduced Boilerplate: Type inference reduces the need for explicit type annotations.
How to Enable Strictly Typed Forms
Strictly typed forms are enabled by default in Angular 14+ when you create a new project. For existing projects, you can update your forms to use the new types by explicitly typing your FormControl
, FormGroup
, and FormArray
.
Example: Strictly Typed Signup Form
Let's say we have not provided any types but still with the help of type inference mechanism, Angular will provide you the best auto-completion while accessing the form values.
But you explicitly provide the type for the singupForm as FormGroup.
signupForm: FormGroup = this.fb.group();
It will loose the auto completion or suggestion when accessing the value. This happens because we have not provided any type to the singupForm and hence it understand it as
singupForm: FormGroup<any> = this.fb.group();
Untyped Forms
In Angular, typed forms were introduced to improve type safety in reactive forms. However, untyped forms are still supported for backward compatibility. Here's a simple explanation:
- Untyped forms work the same way as forms in older Angular versions.
- You must explicitly use the
Untyped
prefix for form classes likeUntypedFormGroup
andUntypedFormControl
.
const loginForm = new UntypedFormGroup({
email: new UntypedFormControl(''), // No type checking
password: new UntypedFormControl(''),
});
If we see the type for this simplest form control
const email = new FormControl('abc@gmail.com');
This control will be automatically inferred to have the type FormControl<string|null>
. TypeScript will automatically enforce this type throughout the FormControl
API, such as email.value
, email.valueChanges
, email.setValue(...)
, etc.
Nullability
Angular's FormControl
includes null
in its type because a control's value can become null
when the reset()
method is called.
For example, if you create a FormControl
with an initial value of 'abc@gmail.com'
and then call reset()
, the value will become null
. This behavior ensures that TypeScript enforces handling the possibility of null
values in your code.
const email = new FormControl('abc@gmail.com');
email.reset();
console.log(email.value); // null
To make a FormControl
non-nullable, you can use the nonNullable
option. When this option is enabled, the control resets to its initial value instead of null
. For instance, if you create a FormControl
with the nonNullable
option and an initial value of 'abc@gmail.com'
, calling reset()
will reset the value back to 'abc@gmail.com'
instead of null
. This approach affects the runtime behavior of your form when reset()
is called and should be used carefully to ensure it aligns with your application's requirements.
const email = new FormControl('abc@gmail.com', {nonNullable: true});
email.reset();
console.log(email.value); // abc@gmail.com
Specifying an Explicit Type
It is possible to specify the type, instead of relying on inference. Consider a control that is initialized to null
. Because the initial value is null
, TypeScript will infer FormControl<null>
, which is narrower than we want.
const email = new FormControl(null);
email.setValue('abc@gmail.com'); // Error!
To prevent this, we explicitly specify the type as string|null
const email = new FormControl<string|null>(null);
email.setValue('abc@gmail.com');
FormArray
: Dynamic, Homogenous Collections
A FormArray
contains an open-ended list of controls. The type parameter corresponds to the type of each inner control:
const names = new FormArray([new FormControl('Rohit')]); names.push(new FormControl('Rolly'));
This FormArray
will have the inner controls type FormControl<string|null>
.
If you want to have multiple different element types inside the array, you must use UntypedFormArray
, because TypeScript cannot infer which element type will occur at which position.
Understanding Angular's Typed Forms: FormGroup, FormRecord, and NonNullableFormBuilder
Angular's reactive forms have evolved significantly with the introduction of typed forms in Angular 14. These enhancements bring type safety, flexibility, and better developer experience to form handling. In this article, we’ll explore key concepts like FormGroup
, FormRecord
, Partial
values, and the NonNullableFormBuilder
.
FormGroup and FormRecord
Angular provides two main types for managing forms:
FormGroup: Used for forms with a predefined set of keys. Each key corresponds to a specific form control.
FormRecord: Designed for forms with dynamic or open-ended keys, where the keys are not known ahead of time.
When to Use FormRecord: If your form needs to handle dynamic keys (e.g., a list of addresses or user-defined fields),FormRecord
is the ideal choice.
Partial Values in FormGroup
In Angular, you can disable form controls, and any disabled control will not appear in the form's value. This behavior makes the form's value a partial object, meaning some fields might be undefined
.
If you disable the password
control:
The type of login.value
becomes:
Here, Partial
means that each field in the object might be undefined
. For example, login.value.password
could be undefined
.
Accessing Raw Values
If you want to include disabled controls in the form's value, you can use the getRawValue()
method. This method bypasses the Partial
type and returns all controls, including disabled ones.
Optional Controls and Dynamic Groups
Some forms may have controls that are optional or added/removed dynamically at runtime. You can represent these controls using optional fields in TypeScript.
Here, TypeScript ensures that only optional controls (like password
) can be removed or added dynamically.
FormRecord for Open-Ended Forms
When the keys of a form are not known in advance, FormRecord
is the best choice. It allows you to dynamically add controls with flexible keys.
If your form needs to handle both dynamic keys and heterogeneous control types, you should use UntypedFormGroup
for maximum flexibility.
NonNullableFormBuilder
The NonNullableFormBuilder
is a shorthand for creating forms where all controls are non-nullable. This eliminates the need to repeatedly specify { nonNullable: true }
for each control.
In this example, both email
and password
controls are non-nullable by default. This is particularly useful for large forms where you want to avoid boilerplate code.
Key Differences Between FormGroup and FormRecord
Feature | FormGroup | FormRecord |
---|---|---|
Keys | Predefined and enumerated. | Dynamic and open-ended. |
Use Case | Fixed forms with known structure. | Dynamic forms with unknown keys. |
Example | Login form with email and password . | Address book with user-defined keys. |
When to Use Typed Forms
Typed forms are ideal for ensuring type safety and reducing runtime errors. They are particularly useful in scenarios where:
- You need strict control over form values and types.
- You want better autocompletion and error detection in your IDE.
- You are working with complex or dynamic forms.
For older projects, you can still use UntypedFormGroup
and UntypedFormControl
for backward compatibility.
Angular's typed forms, including FormGroup
, FormRecord
, and NonNullableFormBuilder
, provide powerful tools for building robust and type-safe forms. Whether you're working with fixed forms or dynamic, open-ended forms, these features ensure better developer experience and maintainability. By leveraging these tools, you can create forms that are both flexible and reliable.
Comments