From 479ffc1d4e76afa2ac91f4171f40342833bcb600 Mon Sep 17 00:00:00 2001 From: SkyZeroZx <73321943+SkyZeroZx@users.noreply.github.com> Date: Sun, 12 Oct 2025 13:29:45 -0500 Subject: [PATCH] docs: Documents utility functions for narrowing form control types (#64373) PR Close #64373 --- .../src/content/guide/forms/reactive-forms.md | 27 +++++++++++++++++++ packages/forms/src/model/form_array.ts | 2 ++ packages/forms/src/model/form_control.ts | 2 ++ packages/forms/src/model/form_group.ts | 4 +++ 4 files changed, 35 insertions(+) diff --git a/adev/src/content/guide/forms/reactive-forms.md b/adev/src/content/guide/forms/reactive-forms.md index 6bc7c784cef..8c091c0174c 100644 --- a/adev/src/content/guide/forms/reactive-forms.md +++ b/adev/src/content/guide/forms/reactive-forms.md @@ -490,6 +490,33 @@ control.events.subscribe((e) => { NOTE: On value change, the emit happens right after a value of this control is updated. The value of a parent control (for example if this FormControl is a part of a FormGroup) is updated later, so accessing a value of a parent control (using the `value` property) from the callback of this event might result in getting a value that has not been updated yet. Subscribe to the `events` of the parent control instead. +## Utility functions for narrowing form control types + +Angular provides four utility functions that help determine the concrete type of an `AbstractControl`. These functions act as **type guards** and narrow the control type when they return `true`, which lets you safely access subtype-specific properties inside the same block. + +| Utility function | Details | +| :--------------- | :-------------------------------------------------- | +| `isFormControl` | Returns `true` when the control is a `FormControl`. | +| `isFormGroup` | Returns `true` when the control is a `FormGroup` | +| `isFormRecord` | Returns `true` when the control is a `FormRecord` | +| `isFormArray` | Returns `true` when the control is a `FormArray` | + +These helpers are particularly useful in **custom validators**, where the function signature receives an `AbstractControl`, but the logic is intended for a specific control kind. + +```ts +import { AbstractControl, isFormArray } from '@angular/forms'; + +export function positiveValues(control: AbstractControl) { + if (!isFormArray(control)) { + return null; // Not a FormArray: validator is not applicable. + } + + // Safe to access FormArray-specific API after narrowing. + const hasNegative = control.controls.some(c => c.value < 0); + return hasNegative ? { positiveValues: true } : null; +} +``` + ## Reactive forms API summary The following table lists the base classes and services used to create and manage reactive form controls. diff --git a/packages/forms/src/model/form_array.ts b/packages/forms/src/model/form_array.ts index 22649bbb98d..a9a5ea33bdb 100644 --- a/packages/forms/src/model/form_array.ts +++ b/packages/forms/src/model/form_array.ts @@ -582,6 +582,8 @@ export const UntypedFormArray: UntypedFormArrayCtor = FormArray; * @description * Asserts that the given control is an instance of `FormArray` * + * @see [Utility functions for narrowing form control types](guide/forms/reactive-forms#utility-functions-for-narrowing-form-control-types) + * * @publicApi */ export const isFormArray = (control: unknown): control is FormArray => control instanceof FormArray; diff --git a/packages/forms/src/model/form_control.ts b/packages/forms/src/model/form_control.ts index 0aa96543a58..1caeec10121 100644 --- a/packages/forms/src/model/form_control.ts +++ b/packages/forms/src/model/form_control.ts @@ -621,6 +621,8 @@ export const UntypedFormControl: UntypedFormControlCtor = FormControl; * @description * Asserts that the given control is an instance of `FormControl` * + * @see [Utility functions for narrowing form control types](guide/forms/reactive-forms#utility-functions-for-narrowing-form-control-types) + * * @publicApi */ export const isFormControl = (control: unknown): control is FormControl => diff --git a/packages/forms/src/model/form_group.ts b/packages/forms/src/model/form_group.ts index 1822ef0fbce..49457405a92 100644 --- a/packages/forms/src/model/form_group.ts +++ b/packages/forms/src/model/form_group.ts @@ -709,6 +709,8 @@ export const UntypedFormGroup: UntypedFormGroupCtor = FormGroup; * @description * Asserts that the given control is an instance of `FormGroup` * + * @see [Utility functions for narrowing form control types](guide/forms/reactive-forms#utility-functions-for-narrowing-form-control-types) + * * @publicApi */ export const isFormGroup = (control: unknown): control is FormGroup => control instanceof FormGroup; @@ -827,6 +829,8 @@ export interface FormRecord { * @description * Asserts that the given control is an instance of `FormRecord` * + * @see [Utility functions for narrowing form control types](guide/forms/reactive-forms#utility-functions-for-narrowing-form-control-types) + * * @publicApi */ export const isFormRecord = (control: unknown): control is FormRecord =>