DEV Community

Victor Shelepen
Victor Shelepen

Posted on

A TypeScript Utility for Type-Safe Angular Forms

Hello, I'd like to share a code snippet that helps keep your Angular forms strongly typed and consistent with your data interfaces. I'm working on a full-stack TypeScript project where a single set of interfaces serves as the foundation for the database models, services, DTOs, and UI components. The following snippet of code implements this concept.

import { FormArray, FormControl, FormGroup } from "@angular/forms";

export type GForm<IInterface> = FormGroup<{
  [Key in keyof IInterface]: 
    IInterface[Key] extends Array<infer ArrayItem>
      ? FormArray<GForm<ArrayItem>> | FormControl<ArrayItem[]> 
      : IInterface[Key] extends Date
        ? FormControl<Date | null>
        : IInterface[Key] extends object
          ? GForm<IInterface[Key] | null> | FormControl<IInterface[Key] | null>
          : FormControl<IInterface[Key] | null>
}>;
Enter fullscreen mode Exit fullscreen mode

Here's how I use it:

export interface ICar {
  brand: string,
  model: string,
}

form: GForm<ICar> = new FormGroup({
  brand: new FormControl<string>('', [Validators.required, Validators.minLength(2)]),
  model: new FormControl<string>('', [Validators.required, Validators.minLength(2)]),
});

export interface IDrivingLicence {
  fullname: string;
  car: ICar
}

form = new FormGroup({
  fullname: new FormControl<string>(''),
  car: new FormControl<ICar>(defaultCar, { nonNullable: true }),
},) as GForm<IDrivingLicence>;
Enter fullscreen mode Exit fullscreen mode

I'm wondering if a similar utility already exists in the Angular library—I may have missed it. I'd appreciate it if someone could point me to a ready-made solution if one exists. My solution isn't perfect, either. It doesn't cover every edge case I've encountered. I would be grateful for any comments or suggestions on how to improve this snippet.

Notes

  • The null type is included because a form's controls can become null when the form is reset. You can still use validators to make null values invalid.
  • FormControl<T[Key] | null> is used for complex objects when you want to manage that object's value with a single custom form control rather than a nested FormGroup.

Top comments (0)