Angular typed reactive forms let TypeScript check the shape and value types of form controls, helping you choose controls that match the data your interface collects. Use a typed FormControl for one value, a FormGroup for named fields, and a FormArray for a variable-length collection; choose nullability and update operations deliberately to avoid common runtime surprises.
What typed reactive forms change
Reactive forms represent form state as objects in your component code. Angular introduced strictly typed reactive forms in version 14; in current Angular projects, the reactive-forms APIs are typed by default. Types help catch mismatched control names and value shapes during development, but they do not validate arbitrary data at runtime or replace form validators.
A form’s value is not always a plain object with every expected property. Disabled controls are omitted from a group’s regular value, so Angular types that value as potentially partial. Use getRawValue() when you specifically need values from disabled controls too. This distinction matters when preparing a complete object for an API or another calculation.
Choose the control by data shape
FormControl<T>: one value, such as a name, date, checkbox state, or selected identifier.FormGroup<TControls>: a known set of named controls, such as a profile with name and email fields.FormArray<TControl>: an ordered collection whose number of controls can change, such as editable phone numbers or line items.FormRecord<TControl>: a group of controls with a common type but keys that are not known in advance, such as settings keyed by a dynamic name.
For example, a fixed profile form can be expressed as new FormGroup({ name: new FormControl('', { nonNullable: true }), email: new FormControl('', { nonNullable: true }) }). The group’s keys are inferred from its controls. If a field is optional in the form structure, mark the control key optional in the group’s control type; this is different from allowing the field’s value to be null.
Set nullability and initial values intentionally
By default, a FormControl includes null in its value type. This reflects the behavior of reset(): without non-nullable configuration, resetting a control can set its value to null. For a required string that should reset to an empty string instead, use { nonNullable: true } or construct the control through NonNullableFormBuilder.
Do not make a control non-nullable just to silence a type error if null is a real state in the interface. For example, a date that has not been selected may genuinely be null. Model that state explicitly, then handle it in code and add validators if the field must be completed before submission. A TypeScript type describes what the program expects; a validator checks whether the current form value meets a rule.
Initial values also determine inference. A control initialized with a string is inferred as a string control (or a string-or-null control under the default behavior). If a control needs a wider type, specify it: new FormControl<number | null>(null) is appropriate for a number that starts empty. Avoid relying on a type inferred from an initial value that is narrower than the values the control will later hold.
Use groups, arrays, and records correctly
A FormGroup suits a stable set of fields. Its setValue() method expects the complete group structure, making it useful when completeness is required. patchValue() accepts partial updates, which is convenient for loading only some fields, but can conceal a missing property if the caller assumes it updated the whole form. Choose based on whether partial data is intentional.
For a repeatable section, use a FormArray and create controls with a factory function. A typed array of groups can represent repeated structured entries, for example FormArray<FormGroup<{ label: FormControl<string>; quantity: FormControl<number> }>>. Add and remove controls through the array’s methods so the form model and rendered fields remain aligned. Use a FormRecord instead when entries have dynamic keys rather than meaningful positions in a list.
Angular’s FormBuilder can reduce repetitive construction. Its non-nullable variant is useful when most controls should reset to their initial values, but individual nullable controls may still be needed. Pick the builder for clarity, not because it changes what a form group or control represents.
Understand values, updates, and validation
Typed forms help with operations such as reading a known control’s value and passing a correctly shaped object to setValue(). They do not guarantee that an HTML input produces a value of the expected type in every circumstance, nor do they prove that user input is valid. Use Angular validators for rules such as required fields or numeric ranges, and check the form’s validity before acting on submitted data.
Be careful with numeric inputs. A control declared as FormControl<number> should be paired with an appropriate value accessor and should have a clear policy for blank input. Do not assume that adding type="number" makes every raw value trustworthy or that a TypeScript annotation converts strings into numbers. Inspect the actual value flow and validate or normalize data at boundaries.
Also consider update timing. A control can update on every change, on blur, or on submit through its update configuration. With delayed updates, reading a value before the configured event may return the previous value. Subscriptions to valueChanges should account for the control’s lifecycle and the possibility of values that are nullable or incomplete.
Common problems and practical checks
- “Object is possibly null”: decide whether null is valid; handle it or configure a genuinely non-nullable control.
- Missing fields in a group value: disabled controls are excluded from
value; usegetRawValue()only when including them is intended. setValue()rejects an object: compare the object with the group’s full control structure. UsepatchValue()for a deliberate partial update.- Wrong control selected: use a group for fixed named fields, an array for a changing ordered list, and a record for dynamic keys of a common control type.
- Types look correct but data is invalid: add validators and verify values received from external sources; compile-time typing does not validate runtime input.
When migrating an older application, Angular’s untyped reactive-form classes remain available for gradual changes. Prefer typed forms for new or migrated areas, and avoid mixing typed and untyped values without making the boundary explicit. The best design is the one whose control structure mirrors the data the interface actually edits.
Frequently Asked Questions
Should I use FormGroup or FormArray?
Use FormGroup for a fixed collection of named controls. Use FormArray when users can add, remove, or reorder entries.
Why does a typed control include null?
Controls are nullable by default because reset() can set them to null. Use non-nullable configuration only when reset should restore the initial value and null is not a valid state.
Does a typed form validate user input?
No. TypeScript checks code at compile time. Angular validators check form rules, and application code should still validate or normalize values received at runtime.
What is the difference between value and getRawValue()?
A group’s value excludes disabled controls and may therefore be partial. getRawValue() includes disabled controls as well.
Can I add controls with dynamic names?
Use FormRecord when keys are dynamic and the controls share a type. Use FormArray when the entries are best represented as an ordered list.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Best CS 1.6 Server Hosting: Options and How to Choose
- Best Secure Web Browsers for Privacy in 2026
- Nothing Phone 1: Specs, Updates and Buying Advice
- To-Do Lists for Mac: Apps, Features, and How to Choose
- Best Privacy Web Browsers: Which One Should You Use?
- Tower Defense Simulator TDS Codes: What Works Now