Forms

Angular Strictly Typed Forms (Complete Guide)

Learn the best way to leverage Angular Typed Forms in your projects. Add type safety to your form code by relying mostly on type inference, without needing to add extra type annotations.

Angular Strictly Typed Forms (Complete Guide) — Forms article by Angular University on Angular In Depth
Angular Strictly Typed Forms (Complete Guide) — Forms article by Angular University on Angular In Depth
On this page · 7 sections

Before Angular 14, Reactive Forms in Angular were not fully type-safe. The value emitted by a form was of type any, and the framework lacked detailed information about the types of individual form controls. This made it easy to accidentally introduce type errors, like assigning a value of the wrong type to a form field.

However, starting with Angular 14, Angular introduced comprehensive built-in type safety for Reactive Forms. This means you can now get meaningful error messages and autocomplete suggestions when working with form values, patching data, and in many other scenarios.

Contrary to what you might expect, you don't need to create custom types or add a lot of type annotations to your form declarations to take advantage of this feature.

You can enjoy all of these type safety benefits with minimal changes to your existing code, as we will see throughout this article.

Topics Covered

This guide will explore the following areas:

  • The Optimal Approach to Angular Typed Forms
  • A Common Pitfall to Avoid with Typed Forms
  • Why Defining Extra Types (or Switching APIs) Isn't Necessary
  • Declaring Nullable Fields
  • Utilizing the NonNullableFormBuilder Service
  • A Brief Recap

This article is part of our ongoing series on Angular Forms. You can find all related posts here.

Let's dive straight into the details of Angular Typed Forms.

The Optimal Approach to Angular Typed Forms

So, what's the best way to use typed forms? My preferred method is to declare reactive forms using the standard FormBuilder API, just as you normally would, without adding type annotations or defining custom types.

Consider a standard login form declaration with two fields (email and password):

As you can see, this is the same pattern we've always used.

Amazingly, type safety is already active, and Angular is aware that this form has two fields, email and password, both of which are strings!

Let's verify this by checking if autocomplete works within the login method:

Angular Typed Forms - Auto-completion example for the form value

As we can see, autocomplete and type safety are available without any extra effort!

Let's look at another example of type safety, this time when using the form's patchValue() API:

Angular Typed Forms - Auto-completion example for patchValue

Again, autocomplete is automatically enabled.

More importantly, we now receive useful compiler error messages if we try to pass an incorrectly typed value to a specific form field:

Angular Typed Forms - Example of helpful compiler error messages

How does this work? It's all thanks to the TypeScript compiler and its robust type inference mechanism.

The compiler infers the names and types of each form field based on the initial value provided for each field.

Since both fields are initialized to an empty string, Angular infers both to be of type string.

To be more accurate, because all form fields are considered nullable by default, the inferred type for email and password is string | null.

We'll delve deeper into nullable fields later in this post.

For now, let's appreciate how powerful Angular Typed Forms are: we get complete type safety without adding any extra type annotations. It's quite remarkable!

However, this benefit is only realized when the FormBuilder is used as intended.

Let's look at a few scenarios where issues might arise.

Avoid This Common Typed Forms Pitfall

A frequent mistake when working with typed forms is to declare your form variable with the explicit type FormGroup, like this:

This seems like a natural approach, right? We declare a form member variable and initialize it in ngOnInit, which is a common and established pattern.

While this declaration style may seem familiar, it's no longer recommended as it prevents you from getting full form type safety.

Notice that we no longer get autocomplete when accessing the form's value!

Angular Typed Forms - Auto-completion gone the form value, due to wrong declaration of the form member variable

Similarly, when using patchValue, we no longer get suggestions for the form field names:

Angular Typed Forms - Auto-completion gone the form value, due to wrong declaration of the form member variable

In addition to losing autocomplete, we also lose helpful compiler error messages when assigning incorrect values to a field.

Why doesn't this work?

This declaration style prevents type safety because we're explicitly typing the form as FormGroup, which effectively defaults to FormGroup<any>. This means the form's value is treated as any.

As we know, any signifies that the compiler has no type information, disabling all type safety checks for the form.

Interestingly, TypeScript still infers all the type information from the form definition, but we discard it by assigning the form to a variable of type FormGroup.

To avoid this issue and maintain full type safety, stick with the method shown first in this article, and let type inference determine the form variable's type.

Why Defining Extra Types (or Switching APIs) Isn't Necessary

Making type-safe forms work does not require you to declare an explicit form type, like this:

This extra type is unnecessary. Relying on type inference is more readable and less verbose.

You also don't need to switch to the constructor API to benefit from type safety.

For instance, you don't have to write your form declarations in the following way:

It's not wrong, but the FormBuilder API is just as type-safe and less verbose, so I generally prefer it.

Declaring Nullable Fields

By default, every form field is treated as nullable. This is because when you call the form's reset() method, Angular sets all fields to null.

This behavior has existed in Angular since its early versions, which is why we must consider each field nullable unless we explicitly configure it otherwise.

You can also choose to declare a form field as non-nullable, which means it will be reset to its initial value rather than null.

One way to do this is by using the constructor API:

In this example, the email field is non-nullable, while the password field remains nullable.

However, this can also be achieved with the FormBuilder API, yielding the same results:

Let's see what happens when we call reset on our form:

Here's the resulting output in the browser's console:

Angular Typed Forms - example of reset of a non-nullable field

As shown, the email field is reset to its initial value instead of null.

Utilizing the NonNullableFormBuilder Service

If you're building a large form where all fields should be non-nullable, marking each one individually can become verbose and tedious.

A more straightforward approach is to switch from FormBuilder to the NonNullableFormBuilder service.

With this service, you can declare fields without specifying nullability, and every field will be considered non-nullable by default:

After calling reset on this form, here is the console output:

Angular Typed Forms - example of reset of a non-nullable field

As you can see, all fields are now treated as non-nullable, resetting to their initial values as expected, rather than to null.

Summary

I hope this post has helped you understand how Angular Typed Forms work and how to use them effectively.

This feature is incredibly convenient!

We now have full type safety and autocomplete in our form-related code, without any extra boilerplate like type annotations or a switch to the constructor API.

This all happens almost magically, thanks to the power of TypeScript type inference.

In conclusion, I highly encourage you to start using Typed Forms in your projects. There's no downside, and it can help you prevent numerous bugs and make your form code more maintainable.

If you'd like to learn more about Angular Forms (both Template-Driven and Reactive), we recommend checking out the Angular Forms In Depth course, where Typed Forms are covered thoroughly.

If you have any questions or comments, please leave them below, and I'll get back to you.

To stay updated on upcoming Angular posts, feel free to subscribe to our newsletter:

And if you're just starting with Angular, be sure to check out the Angular for Beginners Course:

Angular Strictly Typed Forms (Complete Guide) — figure 8
AU
Angular University

Writes about RxJS, Components, Signals. Active 2015–2026.

All 79 articles →