Skip to content

Commit 00aea19

Browse files
authored
Merge pull request #139 from ntziolis/master
feat(ControlsType): added typings for AbstractControls + FormArrays
2 parents 05ad0a8 + 1472ab0 commit 00aea19

File tree

4 files changed

+49
-14
lines changed

4 files changed

+49
-14
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ If you want to see the demo in action, please visit [https://cloudnc.github.io/n
4545

4646
- 2 classes for top level form components: `NgxRootFormComponent`, `NgxAutomaticRootFormComponent`
4747
- 2 classes for sub level form components: `NgxSubFormComponent`, `NgxSubFormRemapComponent`
48-
- 3 interfaces: `Controls<T>`, `ControlsNames<T>`, `FormGroupOptions<T>`
48+
- 7 interfaces: `Controls<T>`, `ControlsNames<T>`, `FormGroupOptions<T>`, `TypedFormGroup<T>`, `TypedFormArray<T>`, `TypedFormControl<T>`, `TypedAbstractControl<T>`
4949
- 1 function: `subformComponentProviders`
5050

5151
So there's actually nothing to setup (like a module), you can just use them directly.

projects/ngx-sub-form/src/lib/ngx-sub-form-utils.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
FormControl,
77
FormArray,
88
AbstractControl,
9+
FormGroup,
910
} from '@angular/forms';
1011
import { InjectionToken, Type, forwardRef, OnDestroy } from '@angular/core';
1112
import { Observable, Subject, timer } from 'rxjs';
@@ -19,7 +20,10 @@ export type ControlsNames<T> = { [K in keyof T]-?: K };
1920

2021
export type ControlMap<T, V> = { [K in keyof T]-?: V };
2122

22-
export type ControlsType<T> = { [K in keyof T]-?: T[K] extends any[] ? FormArray : AbstractControl };
23+
export type ControlsType<T> = {
24+
[K in keyof T]-?: T[K] extends any[] ? TypedFormArray<T[K]> : TypedFormControl<T[K]> | TypedFormGroup<T[K]>;
25+
};
26+
2327
export type FormErrorsType<T> = {
2428
[K in keyof T]-?: T[K] extends any[] ? (null | ValidationErrors)[] : ValidationErrors;
2529
};
@@ -32,6 +36,37 @@ export type FormErrors<FormInterface> = null | Partial<
3236
}
3337
>;
3438

39+
// using set/patch value options signature from form controls to allow typing without additional casting
40+
export interface TypedAbstractControl<TValue> extends AbstractControl {
41+
value: TValue;
42+
valueChanges: Observable<TValue>;
43+
setValue(value: TValue, options?: Parameters<AbstractControl['setValue']>[1]): void;
44+
patchValue(value: Partial<TValue>, options?: Parameters<AbstractControl['patchValue']>[1]): void;
45+
}
46+
47+
export interface TypedFormGroup<TValue> extends FormGroup {
48+
value: TValue;
49+
valueChanges: Observable<TValue>;
50+
controls: ControlsType<TValue>;
51+
setValue(value: TValue, options?: Parameters<FormGroup['setValue']>[1]): void;
52+
patchValue(value: Partial<TValue>, options?: Parameters<FormGroup['patchValue']>[1]): void;
53+
}
54+
55+
export interface TypedFormArray<TValue extends any[]> extends FormArray {
56+
value: TValue;
57+
valueChanges: Observable<TValue>;
58+
controls: TypedAbstractControl<TValue>[];
59+
setValue(value: TValue, options?: Parameters<FormArray['setValue']>[1]): void;
60+
patchValue(value: TValue, options?: Parameters<FormArray['patchValue']>[1]): void;
61+
}
62+
63+
export interface TypedFormControl<TValue> extends FormGroup {
64+
value: TValue;
65+
valueChanges: Observable<TValue>;
66+
setValue(value: TValue, options?: Parameters<FormControl['setValue']>[1]): void;
67+
patchValue(value: Partial<TValue>, options?: Parameters<FormControl['patchValue']>[1]): void;
68+
}
69+
3570
export type KeysWithType<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T];
3671

3772
export type ArrayPropertyKey<T> = KeysWithType<T, Array<any>>;

projects/ngx-sub-form/src/lib/ngx-sub-form.component.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,17 @@ import {
2121
isNullOrUndefined,
2222
ControlsType,
2323
ArrayPropertyKey,
24+
TypedAbstractControl,
25+
TypedFormGroup,
2426
} from './ngx-sub-form-utils';
25-
import { FormGroupOptions, NgxFormWithArrayControls, OnFormUpdate, TypedFormGroup } from './ngx-sub-form.types';
27+
import { FormGroupOptions, NgxFormWithArrayControls, OnFormUpdate } from './ngx-sub-form.types';
2628

27-
type MapControlFunction<FormInterface, MapValue> = (ctrl: AbstractControl, key: keyof FormInterface) => MapValue;
29+
type MapControlFunction<FormInterface, MapValue> = (
30+
ctrl: TypedAbstractControl<any>,
31+
key: keyof FormInterface,
32+
) => MapValue;
2833
type FilterControlFunction<FormInterface> = (
29-
ctrl: AbstractControl,
34+
ctrl: TypedAbstractControl<any>,
3035
key: keyof FormInterface,
3136
isCtrlWithinFormArray: boolean,
3237
) => boolean;
@@ -42,7 +47,7 @@ export abstract class NgxSubFormComponent<ControlInterface, FormInterface = Cont
4247
return null as any;
4348
}
4449

45-
return this.formGroup.controls as ControlsType<FormInterface>;
50+
return (this.formGroup.controls as unknown) as ControlsType<FormInterface>;
4651
}
4752

4853
public get formGroupValues(): Required<FormInterface> {
@@ -147,7 +152,7 @@ export abstract class NgxSubFormComponent<ControlInterface, FormInterface = Cont
147152
return null;
148153
}
149154

150-
const formControls: Controls<FormInterface> = this.formGroup.controls;
155+
const formControls: ControlsType<FormInterface> = this.formGroup.controls;
151156

152157
const controls: Partial<ControlMap<FormInterface, MapValue | MapValue[]>> = {};
153158

@@ -354,7 +359,7 @@ export abstract class NgxSubFormComponent<ControlInterface, FormInterface = Cont
354359
const formControlNames: (keyof FormInterface)[] = Object.keys(this.formControlNames) as (keyof FormInterface)[];
355360

356361
const formValues: Observable<KeyValueForm>[] = formControlNames.map(key =>
357-
this.formGroup.controls[key].valueChanges.pipe(
362+
((this.formGroup.controls[key] as unknown) as AbstractControl).valueChanges.pipe(
358363
startWith(this.formGroup.controls[key].value),
359364
map(value => ({ key, value })),
360365
),

projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { FormControl, FormGroup, ValidationErrors } from '@angular/forms';
22
import { Observable } from 'rxjs';
3-
import { ArrayPropertyKey, ArrayPropertyValue, Controls, FormUpdate } from './ngx-sub-form-utils';
3+
import { ArrayPropertyKey, ArrayPropertyValue, Controls, FormUpdate, TypedFormGroup } from './ngx-sub-form-utils';
44

55
// @deprecated
66
export interface OnFormUpdate<FormInterface> {
@@ -12,11 +12,6 @@ type Nullable<T> = T | null;
1212

1313
export type NullableObject<T> = { [P in keyof T]: Nullable<T[P]> };
1414

15-
export interface TypedFormGroup<FormInterface> extends FormGroup {
16-
controls: Controls<FormInterface>;
17-
value: FormInterface;
18-
}
19-
2015
export type TypedValidatorFn<T> = (formGroup: TypedFormGroup<T>) => ValidationErrors | null;
2116

2217
export type TypedAsyncValidatorFn<T> = (

0 commit comments

Comments
 (0)