Typed Reactive Forms (v14)

📚 What are Typed Forms?

Typed Reactive Forms were introduced in Angular 14 for better type safety:

  • Type inference: Form values are typed, not 'any'
  • Compile-time errors: Catch typos and type mismatches
  • IDE support: Autocomplete for form controls
  • NonNullableFormBuilder: Controls that reset to initial value

🎯 Interview Questions

  • Q1: What is the difference between typed and untyped forms?
  • A: Typed forms have strict typing on .value, providing autocomplete and compile-time type checking. Untyped forms return 'any'.
  • Q2: What is NonNullableFormBuilder?
  • A: A form builder that creates controls with nonNullable: true by default. On reset(), they return to initial value instead of null.
  • Q3: How do you get the raw value vs value of a form?
  • A: .value excludes disabled controls. .getRawValue() includes all controls including disabled ones.
  • Q4: What is the difference between FormControl<string> and FormControl<string | null>?
  • A: FormControl<string> (nonNullable) can never be null. FormControl<string | null> can be reset to null.

🔧 Live Demo - Typed Form

Address (Nested FormGroup)

Form State:

Valid: false

Dirty: false

Touched: false

Form Value (Typed!):

{
  "name": "",
  "email": "",
  "age": null,
  "address": {
    "street": "",
    "city": "",
    "zipCode": ""
  }
}

💻 Typed Forms Code


import { FormControl, FormGroup, Validators } from '@angular/forms';

// Define interfaces for type safety
interface UserForm {
  name: FormControl<string>;
  email: FormControl<string>;
  age: FormControl<number | null>;
}

// Create typed form
userForm = new FormGroup<UserForm>({
  // nonNullable: true - resets to initial value, not null
  name: new FormControl('', { 
    nonNullable: true, 
    validators: [Validators.required] 
  }),
  email: new FormControl('', { 
    nonNullable: true, 
    validators: [Validators.email] 
  }),
  age: new FormControl<number | null>(null), // Can be null
});

// Type-safe access!
const name: string = this.userForm.controls.name.value; // ✅ Typed!
const email = this.userForm.value.email; // string | undefined

// Using NonNullableFormBuilder (shorthand)
import { NonNullableFormBuilder, inject } from '@angular/core';

class MyComponent {
  private fb = inject(NonNullableFormBuilder);
  
  form = this.fb.group({
    name: ['', Validators.required],
    email: ['', Validators.email],
  });
  // All controls are nonNullable by default!
}

// getRawValue() includes disabled controls
const rawValue = this.userForm.getRawValue();