Angular Forms Story: Strong Types
Tags: reactiveforms, angular, typescript, strongtyping
Published: 2020-04-22 14:18:45

---
title: Angular Forms Story: Strong Types
published: true
date: 2020-04-02 15:53:30 UTC
tags: reactive-forms,angular,typescript,strong-typing
canonical_url: https://indepth.dev/angular-forms-story-strong-types/
---

![](https://cdn-images-1.medium.com/max/1024/1*83ZeAMjJiNn9se6GGuyasw.png)

How I strongly typed my Angular forms. And you can too!

_Originally published in Angular-In-Depth_ [_here_](https://indepth.dev/angular-forms-story-strong-types/)_._

This is part of the _Angular Forms Story_ series:

- _Angular Forms Story: Dev Tooling (_[_link_](https://dev.to/gparlakov/angular-forms-story-a-dev-tool-2ejc)_)_
- _Angular Forms Story: Strong Types_ (this one)
- _Angular Forms Story: The Whole Story_ (coming up)

Angular Reactive Forms are **not strongly typed**! AbstractControl and it’s implementations FormControl FormGroup and FormArray do not support strong typing their value or changes or any other property/method. For the longest time, I accepted and worked around that, considering it just one of life’s things — so it goes…

In this article, I want to share the process I went through while gradually **adding strong types** to my forms. If you just want to see the end result see [ngx-forms-typed](https://www.npmjs.com/package/ngx-forms-typed). It’s **strong type, backward compatible** (_works with Angular 2.4.0 !_) and **reduces boilerplate.**

_This work draws inspiration and ideas from_ [_“Working with Angular forms in an enterprise environment”_](https://timdeschryver.dev/blog/working-with-angular-forms-in-an-enterprise-environment) _and_ [_“Building scalable robust and type safe forms with Angular”_](https://dev.to/maxime1992/building-scalable-robust-and-type-safe-forms-with-angular-3nf9) _and others. I would_ **_encourage_** _you to check those articles out._

For my job, I had to do a reasonably large form, enough so to make me fear the **business** logic **mixing** with the **form** logic and sub-parts of the form. I wanted to have a **strongly-typed** , **easy** to use and **@angular/forms_-y_**  **way** to extract sub-forms components from a large form. Much like we extract components to handle their own concerns. The (sub)form component would need to conform to the following requirements:

- have a **strongly typed**  model
- **@angular/forms ** —  **compatible**
- use **existing abstraction** for **communication** (the AbstractControl and the ControlValueAccessor) to handle validation, status changes, etc.

In short, I wanted to have a **component** that could be used as a **single control** in a **larger form** _AND_ be a **form** in its **own right** with multiple fields, validation, status changes. And all the while keeping the **parent form** happily unaware of implementation details and communicating via the AbstractControl API.  
For example Address as a sub-form in a Person form and in an Order form. But the full form in a NewAddress form.

In the beginning, I was decorating my FormGroup-s and their constituent FormControl-s with types, like this:

![](https://cdn-images-1.medium.com/max/918/1*904NyfxnM8nTg3GuJcjd7w.png)

Now I could **rely** on strong typing during refactoring or adding/removing features. Any Person **type change** would trickle down to the form — for example, if the property address was to be added I would get an **error immediately** (_vs run time and maybe_):

![Screenshot describing an error of type: “ Property ‘address’ is missing in type ‘{ name: FormControl; email: FormControl; }’](https://cdn-images-1.medium.com/max/1024/1*TXzVvV0KuClxZO0PQphBqA.png)

Then I suffered the form.controls.get('name') way of reaching my controls. I did not like the pattern of creating a public getter for it. Mainly, I wanted **type safety**. So:

![Screenshot demonstrating `controls` FormGroup property being strong typed.](https://cdn-images-1.medium.com/max/918/1*904NyfxnM8nTg3GuJcjd7w.png)<figcaption>Yaay, intellisense! Productivity!</figcaption>

Now I had intellisense for my controls! Oh joy and productivity 😂⚒👷‍♂️. And with a bit more finesse (_and beating Typescript into submission_) I had a type-dependent form group (or model-dependent if you will). See this little change trickling down:

![](https://cdn-images-1.medium.com/max/1024/1*XKsqKXHA5xqd2509GDRb3g.png)<figcaption>Notice the `email` changed into `emailS` and causing Typescript giving me very helpful advice :)</figcaption>

Did you notice the as unknown as PersonFormGroup (at the end of the form group instantiation form = new Form(…) as unknown …)— that’s what I was referring to as _beating Typescript_. In this case, I knew better than it what the actual shape of the run-time thing is! This is the only case so far usually, Typescript❤ ️knows better!

Taking this work to the next step is to type as much of the FormControl/FormGroup/FormArray types as possible. And the result is the package [ngx-forms-typed](https://www.npmjs.com/package/ngx-forms-typed). It provides the TypedFormControl ([GitHub](https://github.com/gparlakov/forms-typed/blob/21e99c91877746b506dd64ad0e5a127eeed15bac/projects/forms/src/lib/forms-typed.ts#L29-L36) src)TypedFormGroupand TypedFormArray types.

For example, let’s take a look at TypedFormControl<K>:

{% gist https://gist.github.com/gparlakov/0b376f3099f217fc002cf942357a299c %}

It adds a strong type to the value and valueChanges properties of FormControl so that you know what shape the value is when using it. It also strong types the method setValue so that you’d need to pass in a value of the expected type and maybe options — also strong typed. reset method is strongly typed too. See ResetValue<K> [source](https://github.com/gparlakov/forms-typed/blob/12e51ba0f19ce5d812399c8bf1ec4d159247891f/projects/forms/src/lib/forms-typed.ts#L17).

And **helper functions** for creating instances with those types typedFormControl([GitHub src](https://github.com/gparlakov/forms-typed/blob/21e99c91877746b506dd64ad0e5a127eeed15bac/projects/forms/src/lib/forms-typed.ts#L37-L55)), typedFormArray and typedFormGroup which make the creation of forms strongly typed too:

![](https://cdn-images-1.medium.com/max/1024/1*lxN6LNnl8NNgiA3VFvBgLg.png)<figcaption>TS error pushing us towards the correct type of the forms control.</figcaption>

![](https://cdn-images-1.medium.com/max/1024/1*amcHNuZb-ea7iNnKaIH8-A.png)<figcaption>Does intellisense too!</figcaption>

The function itself only instantiates a FormGroup which is to say that it is **compatible** with **existing forms** code and can be used next to it **without breaking**  it:

{% gist https://gist.github.com/gparlakov/9f47639ed5c31740c97a2b93072d753d %}

I wanted to enable forms and sub-forms to communicate i.e. when sub-form get’s touched — touch the parent when it gets invalid — make the parent invalid too. These two are **supported** by the ControlValueAccessor abstraction. I also wanted to force the sub-form to get touched on cue from the parent, in order to show validation, which is **not supported** by ControlValueAccessor. I wanted to use existing ControlValueAccessor-AbstractControl channels of communication.

I came up with a builder-like pattern for interaction with controls of a form group/array. It comes in a function called forEachControlIn (see full code in [GitHub](https://github.com/gparlakov/forms-typed/blob/master/projects/forms/src/lib/forms-util.ts)). Its goal is to make interacting with controls in a form and between forms easy. It **relies** on having **references** to the forms.

![](https://cdn-images-1.medium.com/max/1024/0*5fqB3okBdg2mR-A_.png)

Finally, to avoid boilerplate I tied it all in a ControlValueAccessorConnector (see full code on [GitHub](https://github.com/gparlakov/forms-typed/blob/master/projects/forms/src/lib/control-value-accessor-connector.ts)). It handles all the connection logic between a parent form and a sub-form.

![](https://cdn-images-1.medium.com/max/1024/0*GT3_uWEirg5xKkYB.png)

See a working example [here](https://angular-forms-story-strong-types.stackblitz.io/) and code in Stackblitz:

[angular-forms-story-strong-types - StackBlitz](https://stackblitz.com/edit/angular-forms-story-strong-types?embed=1&file=src/app/party-form/party-form.component.ts&view=editor)

Just like a user would expect, pressing the Submit button shows validation for the whole page. Blurring an input control shows validation only for that control.

There’s an example of a nested form using formGroup directive in the party-form.component and a stand-alone one, using ngModel in app.component.

You can see the library’s readme [here](https://github.com/gparlakov/forms-typed/blob/master/projects/forms/README.md).

This was a short introduction to the library. Would you like to see a deep dive? Vote [here](https://forms.gle/Hm1R2uqFmq4n7q6k8):

<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fdocs.google.com%2Fforms%2Fd%2Fe%2F1FAIpQLSdXsraJB4vHZoHtB-3ZnmkE0v5TxPWyu5O3VWCpQkqoSpJjDA%2Fviewform%3Fembedded%3Dtrue&amp;display_name=Google+Docs&amp;url=https%3A%2F%2Fdocs.google.com%2Fforms%2Fd%2Fe%2F1FAIpQLSdXsraJB4vHZoHtB-3ZnmkE0v5TxPWyu5O3VWCpQkqoSpJjDA%2Fviewform%3Fembedded%3Dtrue&amp;image=https%3A%2F%2Flh4.googleusercontent.com%2FXekPFLjAd_fplIpbIrPbFLbgnf41C_hPoZylIP5Ca6-CBQfB6IA3I7e6wV98FIcb1Xf0DYA%3Dw1200-h630-p&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=google" width="760" height="675" frameborder="0" scrolling="no"><a href="https://medium.com/media/2c1089cfe2771558f59ffd9ec0463dc4/href">https://medium.com/media/2c1089cfe2771558f59ffd9ec0463dc4/href</a></iframe>

This approach (and package) is not the only one and there are several other approaches. Check out the next article in the series where I summarize my research in the @angular/forms space of packages, PRs, and articles.

I am working on a few Open Source Angular Dev Tooling projects. Check them out at:  
[SCuri — Unit test boilerplate automation](https://github.com/gparlakov/scuri/blob/master/README.md) ([_with Enterprise support option too_](https://tidelift.com/subscription/pkg/npm-scuri?utm_source=npm-scuri&utm_medium=referral&utm_campaign=enterprise))  
[ngx-forms-typed — Angular form, only strong typed!](https://github.com/gparlakov/forms-typed/blob/master/projects/forms/README.md)  
 [ngx-show-form-control — Visualize/Edit any FormControl/Group](https://github.com/gparlakov/forms-typed/blob/master/projects/show-form-control/README.md)