Creating Type-Safe Forms in Angular: Advanced Tip 🤯
Tags: angular, frontend, javascript, tutorial
Published: 2023-08-11 16:11:55

When developing complex applications in Angular, one common challenge is dealing with forms and their validation. As applications grow, so does the complexity of forms, leading to potential errors and maintenance difficulties. To address these challenges, Angular provides a powerful form handling module, `@angular/forms`, which allows developers to create dynamic and type-safe forms. In this tutorial, we will explore a helpful utility type created by me called `WithControlsFrom` that enhances the form-building process by providing type-safe controls for form fields.

## Introduction to `WithControlsFrom` type

The `WithControlsFrom` type is a versatile utility that assists in creating type-safe forms using the `@angular/forms ` module. It simplifies the process of binding form controls to an interface, ensuring that the form's structure aligns with the interface properties. This utility type generates a type that defines each property of the interface as an optional form control. This way, you can avoid common errors like assigning an incorrect data type to a form control or referencing a non-existing property.

Let's dive into an example to see how `WithControlsFrom` can be used effectively.

## Example: `UserModel` Form

Suppose we have an interface `UserModel` representing user data:

```typescript
export interface UserModel {
  id: number | null;
  name: string;
  gender: string;
}
```

We want to create a form for capturing user information using the `@angular/forms` module. Here's how we can utilize the `WithControlsFrom` type to achieve this:

```typescript
import { Component } from '@angular/core';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';

// 👇 This bind any interface with the FormGroup generating a type-safe form matching existing keys and FormControl type 🙂
export type WithControlsFrom<T> = {
  [P in keyof T]?: FormControl<T[P]> | FormArray<FormControl<T[P]>> | FormGroup<{ [K in keyof T]: FormControl<T[K]> }>;
};

@Component({
  selector: 'app-user-form',
  template: `
    <form [formGroup]="userForm">
      <label>
        ID:
        <input type="number" formControlName="id" />
      </label>
      <label>
        Name:
        <input type="text" formControlName="name" />
      </label>
      <label>
        Gender:
        <input type="text" formControlName="gender" />
      </label>
    </form>
  `,
})
export class UserFormComponent {
  userForm!: FormGroup<WithControlsFrom<UserModel>>;

  constructor(private readonly _formBuilder: FormBuilder) {
    this.userForm = this._formBuilder.group<WithControlsFrom<UserModel>>({
      id: this._formBuilder.control(null),
      name: this._formBuilder.control('Father Horse'),
      gender: this._formBuilder.control(777), // Throws: Type 'FormControl<number>' is not assignable to type 'FormControl<string>'. Type 'number' is not assignable to type 'string'.
      xyz: this._formBuilder.control(''), // Throws: Object literal may only specify known properties, and 'xyz' does not exist in type 'WithKeysFrom<UserModel>'.
    });
  }
}

```

In this example, we've created a `UserFormComponent` that defines a form group named `userForm`. The form group is constructed using the `WithControlsFrom` type, which ensures that the form controls correspond to the properties of the `UserModel` interface. Each property is mapped to a corresponding form control, and the form's HTML template uses the `formControlName` directive to bind the form controls to the input elements.

## Conclusion

In this tutorial, we explored the `WithControlsFrom` utility type, which greatly simplifies the process of creating type-safe forms in Angular applications. By leveraging this utility, developers can ensure that their form controls align with the structure of the underlying data model, reducing the risk of errors and improving code maintainability. This type-safe approach to form creation enhances the development experience and contributes to the overall robustness of Angular applications.

For more information on working with forms in Angular, you can refer to the official Angular documentation.

Remember that using `WithControlsFrom` can significantly enhance the reliability and maintainability of your forms, ensuring that your code remains consistent with your data models. Happy coding!

### References:

- [Angular Forms Documentation ↩](https://angular.io/guide/reactive-forms) 
- [TypeScript Generics ↩](https://www.typescriptlang.org/docs/handbook/2/generics.html)

### Disclaimer
This article was made with a little help from ChatGPT.