ngx-typesafe-forms icon indicating copy to clipboard operation
ngx-typesafe-forms copied to clipboard

Can the FormGroup.controls property be typed ?

Open Xample opened this issue 4 years ago • 4 comments

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 ;-)

Xample avatar Sep 24 '21 11:09 Xample

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

Xample avatar Nov 12 '21 11:11 Xample

Thanks for the suggestion! I will take a look tomorrow to have this improved in the lib!

dirkluijk avatar Nov 16 '21 23:11 dirkluijk

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.

Xample avatar Nov 19 '21 08:11 Xample

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 type as an input
  • a FormControl taking an object with multiple properties must be typed using an interface (of course a Class will work as well… )

Xample avatar Nov 19 '21 10:11 Xample