Package Exports
- angular-typesafe-reactive-forms-helper
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (angular-typesafe-reactive-forms-helper) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
angular-typesafe-reactive-forms-helper
Quick Syntax
Instead of:
this.form.get('heroName').patchValue('He-Man');angular-typesafe-reactive-forms-helper allows:
this.form.getSafe(x => x.heroName).patchValue('He-Man');Why
- Get intellisense
- No more misspelled property names
- Refactoring Reactive Forms is back to a trivial IDE rename task
Demo
In order to make this work as closely as possible to the Angular way, an abstract class FormGroupTypeSafe<T> was derived from Angular’s FormGroup with the intent not to break existing code.
Intellisense on FormGroupTypeSafe

Intellisense on FormGroupTypeSafe

How to use:
1. Define an interface of your form model.
//interface used with FormGroupTypeSafe<T>
interface IHeroFormModel {
name: string;
secretLairs: Array<Address>;
power: string;
sidekick: string
}2. Declare your new FormGroupTypeSafe form with the help of TypeScript’s generics.
/* TypeSafe Reactive Forms Changes */
//old code
//heroForm: FormGroup;
heroForm: FormGroupTypeSafe<IHeroFormModel>;
3. Inject FormBuilderTypeSafe
constructor(
/* TypeSafe Reactive Forms Changes */
//old code - private fb: FormBuilder,
private fb: FormBuilderTypeSafe,
private heroService: HeroService) {
this.createForm();
this.logNameChange();
}4. Create your form group with Interfaces (contracts).
// old code
// this.heroForm = this.fb.group({
// name: '',
// secretLairs: this.fb.array([]),
// power: '',
// sidekick: ''
// });
this.heroForm = this.fb.group<IHeroFormModel>({
name: new FormControl(''),
secretLairs: new FormControl([]),
power: new FormControl(''),
sidekick: new FormControl('')
});
//***** Nested type sample *****
interface IAddressModel {
suburb: string;
postcode: string;
}
interface ICustomerModel {
name: string;
address: IAddressModel;
}
this.form = this.fb.group<ICustomerModel>({
name: new FormControl(null, [Validators.required]),
address: this.formBuilder.group<IAddressModel>({
suburb: new FormControl(''),
postcode: new FormControl('', [Validators.required]),
})
});Peer Dependencies
@angular/forms and all its peer dependencies.
This package has been tested with Angular 9, 10, 11.
(Should work with Angular 4, 5, 6, 7, 8 too)
I would encourage you to use versions Angular still support, see Angular's Support policy and schedule.
Blog
For a more in detail description of the benefits of this package, read my blog - Angular typesafe reactive forms.
When reading the blog, be mindful that it was written Oct-2017, before the angular-typesafe-reactive-forms-helper package existed. Back then, the idea was to copy the code and adjust as needed. Since then, there were a few requests, thus angular-typesafe-reactive-forms-helper was born.
Contributions
I only added features required by my projects, but I know more could be added with your help.
Create a PR to get the conversation started 😄
Lastly
Use it…don’t use it 😄
Release notes
The model used for all code samples:
interface HeroFormModel {
heroName: string;
weapons: WeaponModel[];
}
interface WeaponModel {
name: string;
damagePoints: number;
}FormGroupTypeSafe<T> extends Angular's FormGroup class
V2.0.2 (2021-05-18)
- Bump to Angular 11.
- Stop integration tests for Angular 8.
V2.0.1 (2020-12-09)
Package the correct library files, instead of the repository - rookie mistake :)
V2.0.0 (2020-11-06)
- use ng-packagr to fix bug - main.ts:15 Error: Angular JIT compilation failed
- add end-to-end-tests Angular 8, 9, 10
- removed Angular 7 integration tests from build pipeline as it is no longer supported by Angular team
New dist file structure:
./dist:
LICENSE
README.md
angular-typesafe-reactive-forms-helper.d.ts
angular-typesafe-reactive-forms-helper.metadata.json
bundles
esm2015
fesm2015
package.json
public_api.d.ts
src
./dist/bundles:
angular-typesafe-reactive-forms-helper.umd.js
angular-typesafe-reactive-forms-helper.umd.js.map
angular-typesafe-reactive-forms-helper.umd.min.js
angular-typesafe-reactive-forms-helper.umd.min.js.map
./dist/esm2015:
angular-typesafe-reactive-forms-helper.js
public_api.js
src
./dist/esm2015/src:
angularTypesafeReactiveFormsHelper.js
getPropertyName.js
./dist/fesm2015:
angular-typesafe-reactive-forms-helper.js
angular-typesafe-reactive-forms-helper.js.map
./dist/src:
angularTypesafeReactiveFormsHelper.d.ts
getPropertyName.d.tsOld dist file structure:
./dist:
LICENSE
README.md
lib
package.json
./lib:
angularTypesafeReactiveFormsHelper.d.ts
angularTypesafeReactiveFormsHelper.js
getPropertyName.d.ts
getPropertyName.jsV1.8.2 (2020-09-04)
- Fix bug - getSafe() call fails and returns null when compiled to ES5.
V1.8.1 (2020-06-26)
- Bump to Angular 10.
- Stop integration tests for Angular 6.
V1.8.0 (2020-06-16)
- added
removeControlSafe
Sample:
let sut: FormGroupTypeSafe<HeroFormModel> = createGroup();
sut.removeControlSafe(x => x.heroName);The bottom code was avoided simply because in a variable rename scenario, the IDE should rename all the references instead of just informing one where the errors are.
removeControl(name: keyof T): void;
removeControl(name: string): void;V1.7.0 (2020-05-14)
- added
controls
Angular's forms.d.ts:
controls: { [key: string]: AbstractControl; };angular-typesafe-reactive-forms-helper:
controls: { [P in keyof T]: AbstractControlTypeSafe<T[P]> };Code samples:
let sut: FormGroupTypeSafe<HeroFormModel> = createGroup();
// $ExpectType { heroName: AbstractControlTypeSafe<string>; weapons: AbstractControlTypeSafe<WeaponModel[]>; }
sut.controls;
// $ExpectType AbstractControlTypeSafe<string>
sut.controls.heroName;
// $ExpectType AbstractControlTypeSafe<WeaponModel[]>
sut.controls.weapons;
// $ExpectType string
sut.controls.heroName.value;
// $ExpectType WeaponModel[]
sut.controls.weapons.value;V1.6.0 (2020-04-22)
- added
statusChangesandstatus
Angular's forms.d.ts:
/**
* The validation status of the control. There are four possible
* validation status values:
*
* * **VALID**: This control has passed all validation checks.
* * **INVALID**: This control has failed at least one validation check.
* * **PENDING**: This control is in the midst of conducting a validation check.
* * **DISABLED**: This control is exempt from validation checks.
*
* These status values are mutually exclusive, so a control cannot be
* both valid AND invalid or invalid AND disabled.
*/
readonly status: string;angular-typesafe-reactive-forms-helper:
export type ControlStatus = 'VALID' | 'INVALID' | 'PENDING' | 'DISABLED';
export interface FormGroupTypeSafe<T> extends FormGroup {
readonly status: ControlStatus;
readonly statusChanges: Observable<ControlStatus>;
}Code samples:
let sut: FormGroupTypeSafe<HeroFormModel> = createGroup();
// $ExpectType ControlStatus
sut.status;
sut.statusChanges.subscribe(val => {
// $ExpectType ControlStatus
val;
});
// $ExpectType string | undefined
sut.getSafe(x => x.heroName)?.status; // unfortunately this is still string ¯\_(ツ)_/¯
sut.getSafe(x => x.heroName)?.statusChanges.subscribe(val => {
// $ExpectType ControlStatus
val;
});
V1.5.1 (2020-04-17)
Had this error in Angular 9.1.2 when executing ng serve.
The app would show a blank page with an error in browser's devtools console:
main.ts:15 Error: Angular JIT compilation failed: '@angular/compiler' not loaded!
- JIT compilation is discouraged for production use-cases! Consider AOT mode instead.
- Did you bootstrap using '@angular/platform-browser-dynamic' or '@angular/platform-server'?
- Alternatively provide the compiler with 'import "@angular/compiler";' before bootstrapping.
at getCompilerFacade (core.js:643)
at Function.get (core.js:16349)
at getFactoryDef (core.js:2200)
at providerToFactory (core.js:17183)
at providerToRecord (core.js:17165)
at R3Injector.processProvider (core.js:16981)
at core.js:16960
at core.js:1400
at Array.forEach (<anonymous>)
at deepForEach (core.js:1400)This is fixed.
More info on the error from StackOverflow.
V1.5.0 (2020-04-15)
Extend AbstractControlTypeSafe<P> with:
readonly valueChanges: Observable<T>;
get(path: Array<string> | string): AbstractControl | null;
get(path: number[]): AbstractControlTypeSafe<T extends (infer R)[] ? R : T> | null;- Samples
readonly valueChanges: Observable<T>;:
let sut: FormGroupTypeSafe<HeroFormModel> = createGroup();
sut.valueChanges.subscribe(val => {
// $ExpectType HeroFormModel
val;
});
sut.getSafe(x => x.heroName).valueChanges.subscribe(val => {
// $ExpectType string
val;
});- Split Angular's
getinto two functions based on thepath: Array<string | number> | stringparameter.
Angular's forms.d.ts:
get(path: Array<string | number> | string): AbstractControl | null;angular-typesafe-reactive-forms-helper:
get(path: Array<string> | string): AbstractControl | null;
get(path: number[]): AbstractControlTypeSafe<T extends (infer R)[] ? R : T> | null;This allows type safety when working with arrays.
sut.getSafe(x => x.weapons).get([0]).valueChanges.subscribe(val => {
// $ExpectType WeaponModel
val;
});
// the angular way - .get('person.name')
sut.getSafe(x => x.weapons).get('person.name').valueChanges.subscribe(val => {
// $ExpectType any
val;
});
// the angular way - .get(['person', 'name'])
sut.getSafe(x => x.weapons).get(['person', 'name']).valueChanges.subscribe(val => {
// $ExpectType any
val;
});V1.4.0 (2020-04-14)
new interface
AbstractControlTypeSafe<P>which extends from Angular'sAbstractControland will, over time, contain the common properties to Angular'sFormGroup,FormControlandFormArray. Currently it only returnsreadonly value: T.enhanced
getSafeto returnAbstractControlTypeSafe<P>
getSafe<P>(propertyFunction: (typeVal: T) => P): AbstractControlTypeSafe<P> | null;Code example:
// heroName: string
sut.getSafe(x => x.heroName)?.value; // value's ExpectType => string | undefined- add new type
RecursivePartial<T> - enhanced
patchValueto useRecursivePartial<T>so one is not forced by the compiler to complete mandatory properties on a nested types.
patchValue(value: RecursivePartial<T>, options?: Object): void;Code Example:
typescript let sut: FormGroupTypeSafe<HeroFormModel> = formBuilderTypeSafe.group<HeroFormModel>({...}) // let's pretend a valid FormGroupTypeSafe object was created here // Looking at the line below... // Before V1.4.0, Typescript would have complained about missing property damagePoints. // This is not the case anymore as now all nested types will be Partial properties. sut.patchValue({ weapons: [{ name: "Head" }]});
V1.3.0 (2020-04-06)
- patchValue
Angular's forms.d.ts:
patchValue(value: any, options?: Object): void;angular-typesafe-reactive-forms-helper:
patchValue(value: Partial<T>, options?: Object): void;- formBuilderTypeSafe.group<T> supports
FormArray
sut = formBuilderTypeSafe.group<HeroFormModel>({
heroName: new FormControl('He-Man', Validators.required),
weapons: new FormArray([formBuilderTypeSafe.group<WeaponModel>({
name: new FormControl('Sword', Validators.required),
damagePoints: new FormControl(50, Validators.required)
}),
formBuilderTypeSafe.group<WeaponModel>({
name: new FormControl('Shield', Validators.required),
damagePoints: new FormControl(0, Validators.required)
}),
])
});V1.2.0 (2020-04-02)
- valueChanges, function returns Observable<T>
Angular's forms.d.ts:
typescript valueChanges: Observable<any>;
angular-typesafe-reactive-forms-helper:
typescript valueChanges: Observable<T>;
V1.1.0 (2020-03-31)
- setValue, just a function signature update.
Angular's forms.d.ts function signature:
setValue(value: {
[key: string]: any;
}, options?: {
onlySelf?: boolean;
emitEvent?: boolean;
}): void;angular-typesafe-reactive-forms-helper signature:
setValue(value: T,
options?: {
onlySelf?: boolean;
emitEvent?: boolean
}): void;V1.0.0 (2020-03-29)
angular-typesafe-reactive-forms-helper has these extra functions:
- getSafe
- setControlSafe