ngx-typesafe-forms
ngx-typesafe-forms copied to clipboard
Can the FormGroup.controls property be typed ?
Hello, I am just wondering if there is a technical or strategic reason for:
https://github.com/dirkluijk/ngx-typesafe-forms/blob/master/projects/ngx-typesafe-forms/src/lib/form-group.ts#L33
public controls!: {
[key: string]: AngularAbstractControl;
};
no to be typed as
public controls!: {
[K in keyof T]: AbstractControl<T[K]>;
};
?
This would strongly make sense
BTW: thank you for this lib, I am still wondering why angular does not have this by default ;-)
For those who might be interested in typing the controls, here is the class I am using: form-group.ts
import { FormArray, FormControl, FormGroup as TypedFormGroup } from 'ngx-typesafe-forms';
type FormGroupControls<T> = {
// eslint-disable-next-line @typescript-eslint/ban-types
[K in keyof T]: T[K] extends any[] ? FormArray<T[K]> : T[K] extends extends Record<string, unknown> ? FormGroup<T[K]> : FormControl<T[K]>;
};
/**
* Use this class to type the form groups, import it instead of ngx-typesafe-forms
* see https://github.com/dirkluijk/ngx-typesafe-forms/issues/37
*/
export class FormGroup<T> extends TypedFormGroup<T> {
controls: FormGroupControls<T>;
}
I am assuming:
- any array to be a FormArray
- any object to be a FormGroup
- anything else a FormControl
It is safer and cleaner than casting everything to AbstractControl<T>; as I suggested in my first comment
Thanks for the suggestion! I will take a look tomorrow to have this improved in the lib!
I just updated my post replacing extends object with extends Record<string, unknown> as we should only rely on the fact we do have a dictionary and not an object. For instance the Date being an object was cast to FormGroup instead of FormControl.
oh and before I forget, in order to use properly Record<string, unknown> you need to distinguish Type Aliases from Interfaces. Because both will not be cast the same way.
For instance:
interface anInterface {
"hello": string;
}
type aType = {
"hello": string;
}
// an interface does not extends `Record<string, unknown>`
type castToFormControl = anInterface extends Record<string, unknown> ? true: false; // castToFormControl = false
// while the type aliases does
type castToFormControl2 = aType extends Record<string, unknown> ? true: false; // castToFormControl2 = true
It means:
- a FormGroup should have a
typeas an input - a FormControl taking an object with multiple properties must be typed using an
interface(of course a Class will work as well… )