Home > Net >  Angular/Typescript : How to properly initialize a FormControl of a union type?
Angular/Typescript : How to properly initialize a FormControl of a union type?

Time:09-28

I have an issue with type-safety while initializing a form. My problem is the following :

I basically have an interface to describe a TypedForm with a property that should accept either Enum values or the string values of the Enum.

Here's for the Enum :

export enum MyEnum {
    FIRST_VALUE= 'FIRST_VALUE',
    SECOND_VALUE= 'SECOND_VALUE',
}

Here's for the Type that accepts either Enum or string values :

// similar to MyEnum | 'FIRST_VALUE' | 'SECOND_VALUE'
export type MyEnumKeyValues = MyEnum | keyof typeof StringEnum;

And the Form definition :

export type MyForm = FormGroup<{
    enumProperty: FormControl<MyEnumKeyValues>
}>

The problem is that, when i use FormBuilder to initialize my Form, the default value i pass in always throws a type error :

const myFormGroup: MyForm = this.formBuilder.group({
    // TS2322 :  Type FormControl<MyEnum> is not assignable to FormControl<MyEnumKeyValues>
    enumProperty: MyEnum.FIRST_VALUE,
    // TS2322 :  Type FormControl<string> is not assignable to FormControl<MyEnumKeyValues>
    enumProperty: 'FIRST_VALUE',
    // Works but works with pretty much anything, that's not want we want here...
    enumProperty: 42 as MyEnumKeyValues,
}); 

Note that this works but it's ugly (my real life object is much bigger, with several Enums in it) :

const myFormGroup: MyForm = this.formBuilder.group({
    enumProperty: new FormControl<MyEnumKeyValues>(MyEnum.FIRST_VALUE)
}); 

Is there a better / more elegant way to achieve this please ? Thank you!

CodePudding user response:

The issue here is that the generic type TValue in FormControl is used as a function parameter in multiple methods. This makes the generic type contra-variant which leads to an error when trying to assign FormControl<MyEnum.FIRST_VALUE> to FormControl<MyEnumKeyValues>.

One possible solution here would be to change the type of myForm to

export type MyForm = {
    enumProperty: MyEnumKeyValues
}

and using it when building the form like this:

const myFormGroup = formBuilder.group<MyForm>({
    enumProperty: MyEnum.FIRST_VALUE,
}); 

enumProperty will be type checked to be one of MyEnumKeyValues.

The type of myFormGroup is automatically inferred to

const myFormGroup: FormGroup<{
    enumProperty: FormControl<MyEnumKeyValues>;
}>

Playground

  • Related