What Are Typed Reactive Forms in Angular?
1. What Are Typed Reactive Forms?
Typed Reactive Forms are Angular Reactive Forms where TypeScript knows the exact data type of each form control.
For example:
name: FormControl<string>
age: FormControl<number>
isActive: FormControl<boolean>
Because Angular knows these types, TypeScript can detect incorrect assignments at compile time.
FormControl<string>
↓
Only string values
FormControl<number>
↓
Only number values
Typed Reactive Forms make forms type-safe and reduce runtime mistakes.
2. Full Simple Sample Program
app.ts
import { Component } from '@angular/core';
import {
FormControl,
FormGroup,
ReactiveFormsModule,
Validators
} from '@angular/forms';
@Component({
selector: 'app-root',
standalone: true,
imports: [ReactiveFormsModule],
templateUrl: './app.html'
})
export class App {
employeeForm = new FormGroup({
name: new FormControl('', {
nonNullable: true,
validators: [Validators.required]
}),
age: new FormControl(0, {
nonNullable: true,
validators: [Validators.required, Validators.min(18)]
}),
email: new FormControl('', {
nonNullable: true,
validators: [Validators.required, Validators.email]
}),
isActive: new FormControl(true, {
nonNullable: true
})
});
submitForm() {
if (this.employeeForm.valid) {
const employee = this.employeeForm.getRawValue();
console.log(employee);
console.log(employee.name);
console.log(employee.age);
console.log(employee.email);
console.log(employee.isActive);
} else {
this.employeeForm.markAllAsTouched();
}
}
}
app.html
<h2>Employee Form</h2>
<form
[formGroup]="employeeForm"
(ngSubmit)="submitForm()">
<div>
<label>Name:</label>
<input
type="text"
formControlName="name">
@if (
employeeForm.controls.name.touched &&
employeeForm.controls.name.hasError('required')
) {
<p>Name is required.</p>
}
</div>
<br>
<div>
<label>Age:</label>
<input
type="number"
formControlName="age">
@if (
employeeForm.controls.age.touched &&
employeeForm.controls.age.hasError('min')
) {
<p>Age must be at least 18.</p>
}
</div>
<br>
<div>
<label>Email:</label>
<input
type="email"
formControlName="email">
@if (
employeeForm.controls.email.touched &&
employeeForm.controls.email.hasError('email')
) {
<p>Please enter a valid email.</p>
}
</div>
<br>
<div>
<label>
<input
type="checkbox"
formControlName="isActive">
Active
</label>
</div>
<br>
<button type="submit">
Submit
</button>
</form>
3. How Is This Form Typed?
Consider:
name: new FormControl('', {
nonNullable: true
})
The initial value is:
''
which is a string.
Angular/TypeScript therefore infers:
FormControl<string>
Similarly:
age: new FormControl(0, {
nonNullable: true
})
becomes:
FormControl<number>
and:
isActive: new FormControl(true, {
nonNullable: true
})
becomes:
FormControl<boolean>
So conceptually:
employeeForm
│
├── name
│ └── FormControl<string>
│
├── age
│ └── FormControl<number>
│
├── email
│ └── FormControl<string>
│
└── isActive
└── FormControl<boolean>
4. Why Do We Need Typed Reactive Forms?
Suppose age should contain a number.
With typed forms:
this.employeeForm.controls.age.setValue(25);
This is valid.
But:
this.employeeForm.controls.age.setValue('Twenty Five');
TypeScript reports a compile-time error because:
Expected:
number
Received:
string
This catches mistakes while developing the application rather than waiting for runtime.
5. Type Inference
You don't always have to explicitly write:
FormControl<string>
Angular can infer it.
For example:
const name = new FormControl('Syed');
TypeScript understands the control's type based on the initial value.
However, there is an important detail:
const name = new FormControl('Syed');
is normally:
FormControl<string | null>
rather than simply:
FormControl<string>
Why?
Because Angular allows:
name.reset();
and a nullable control normally resets to:
null
6. Typed Forms and null
This is one of the most important concepts in Typed Reactive Forms.
Consider:
const name = new FormControl('Syed');
Its type is generally:
FormControl<string | null>
Therefore:
name.value
can be:
"Syed"
or
null
7. Using nonNullable
If you don't want null, use:
const name = new FormControl('Syed', {
nonNullable: true
});
Now its type is:
FormControl<string>
instead of:
FormControl<string | null>
This also affects reset behavior.
Nullable
const name = new FormControl('Syed');
name.reset();
Value becomes:
null
Non-nullable
const name = new FormControl('Syed', {
nonNullable: true
});
name.reset();
Value returns to:
Syed
the initial value.
8. Typed FormGroup
You can also explicitly declare the type of the complete form.
interface EmployeeForm {
name: FormControl<string>;
age: FormControl<number>;
email: FormControl<string>;
isActive: FormControl<boolean>;
}
Then:
employeeForm = new FormGroup<EmployeeForm>({
name: new FormControl('', {
nonNullable: true
}),
age: new FormControl(0, {
nonNullable: true
}),
email: new FormControl('', {
nonNullable: true
}),
isActive: new FormControl(true, {
nonNullable: true
})
});
Now the entire form structure is strongly typed.
FormGroup<EmployeeForm>
│
├── FormControl<string>
├── FormControl<number>
├── FormControl<string>
└── FormControl<boolean>
9. Typed Forms with NonNullableFormBuilder
For many forms, a convenient modern approach is using:
NonNullableFormBuilder
Instead of repeatedly writing:
{
nonNullable: true
}
we can use the builder.
Example
import { Component, inject } from '@angular/core';
import {
NonNullableFormBuilder,
ReactiveFormsModule,
Validators
} from '@angular/forms';
@Component({
selector: 'app-root',
standalone: true,
imports: [ReactiveFormsModule],
templateUrl: './app.html'
})
export class App {
private fb = inject(NonNullableFormBuilder);
employeeForm = this.fb.group({
name: ['', Validators.required],
age: [0, [
Validators.required,
Validators.min(18)
]],
email: ['', [
Validators.required,
Validators.email
]],
isActive: [true]
});
submitForm() {
const employee = this.employeeForm.getRawValue();
console.log(employee);
}
}
Angular infers:
name → string
age → number
email → string
isActive → boolean
This avoids a lot of:
string | null
number | null
boolean | null
handling.
10. Typed FormArray
FormArray can also be typed.
For example, a list of skills:
skills = new FormArray<FormControl<string>>([
new FormControl('C#', {
nonNullable: true
}),
new FormControl('Angular', {
nonNullable: true
})
]);
Now every item must be:
FormControl<string>
This is valid:
this.skills.push(
new FormControl('SQL', {
nonNullable: true
})
);
The type system prevents incompatible controls from being added.
11. Typed vs Untyped Reactive Forms
Angular also has legacy untyped APIs such as:
UntypedFormControl
UntypedFormGroup
UntypedFormArray
UntypedFormBuilder
The conceptual difference is:
| Typed Forms | Untyped Forms |
|---|---|
FormControl<string> |
UntypedFormControl |
| Compile-time type checking | Much less type checking |
| Better IntelliSense | Less precise IntelliSense |
| Safer refactoring | More runtime risk |
| Modern/default approach | Mainly migration/legacy use cases |
For new applications, typed reactive forms are generally the appropriate default.
12. Typed Forms Do Not Mean Validation
This is an important interview distinction.
Typing:
FormControl<number>
means:
The control's value should have the TypeScript type
number.
Validation:
Validators.min(18)
means:
The number must be at least 18.
These solve different problems.
Type Safety
↓
FormControl<number>
↓
Must be a number
Validation
↓
Validators.min(18)
↓
Must be >= 18
You often use both together:
age: new FormControl(0, {
nonNullable: true,
validators: [
Validators.required,
Validators.min(18)
]
})
13. Advantages
Typed Reactive Forms provide:
- Compile-time type safety
- Better IDE IntelliSense
- Earlier detection of programming mistakes
- Safer
setValue()andpatchValue()usage - Safer form refactoring
- Strongly typed form values
- Better maintainability for large applications
- Strong integration with TypeScript's type system
14. Key Points
- Typed Reactive Forms provide type-safe Angular forms.
- Angular's standard reactive form APIs support typing.
FormControl<string>represents a string control.FormControl<number>represents a numeric control.FormGroupcan have a strongly typed control structure.FormArraycan also be strongly typed.- Angular can infer many control types from initial values.
- Normal
FormControlvalues often includenull. nonNullable: trueremovesnullfrom the control's value type and changes reset behavior.NonNullableFormBuildermakes non-nullable typed forms more convenient.- Type safety and validation are different concepts.
- Typed forms catch many programming mistakes during development.
15. Interview Questions and Answers
1. What are Typed Reactive Forms?
Typed Reactive Forms are Angular Reactive Forms that use TypeScript types for form controls, groups, arrays, and their values, providing compile-time type safety.
2. Give an example of a typed control.
const name = new FormControl<string>('', {
nonNullable: true
});
Here name is:
FormControl<string>
3. What is the advantage of typed forms?
They provide compile-time checking and better IntelliSense.
For example, a FormControl<number> prevents accidentally assigning a string through the typed API.
4. Why does FormControl('Syed') normally include null?
Because a normal control can be reset:
control.reset();
and its value can become null.
Therefore its inferred type is typically:
FormControl<string | null>
5. What does nonNullable: true do?
It creates a non-nullable control:
FormControl<string>
instead of:
FormControl<string | null>
It also causes reset() to restore the initial value rather than resetting to null.
6. What is NonNullableFormBuilder?
It is a convenient FormBuilder variant that creates non-nullable controls by default.
private fb = inject(NonNullableFormBuilder);
7. Are Typed Reactive Forms the same as validation?
No.
Typing
↓
What type of value?
string / number / boolean
Validation
↓
Is that value acceptable?
required / min / max / email
For example:
FormControl<number>
provides type safety, while:
Validators.min(18)
provides runtime form validation.
Interview Summary
Typed Reactive Forms provide compile-time type safety for Angular forms. Controls can have explicit or inferred types such as
FormControl<string>orFormControl<number>. This improves IntelliSense, catches type errors during development, and makes form code safer to maintain. Since normal controls may containnull,nonNullable: trueorNonNullableFormBuildercan be used when null values are not desired.