My InnerComponent
requires a FormArray
as input, and in order to access the type of the controls within the array, I have introduced a type parameter called ControlValue
. The input is declared to be of type
FormArray<AbstractControl<ControlValue>>
. I opted to use AbstractControl instead of FormControl to allow for flexibility in the structure of values within the array.
export class InnerComponent<ControlValue> {
@Input({ required: true }) public formArray!: FormArray<AbstractControl<ControlValue>>
}
However, when I attempt to assign a value of type
FormArray<FormControl<number | null>>
to the formArray
in InnerComponent
, TypeScript resolves the type parameter ControlValue
to number | null
, but I encounter a compilation error:
Type
FormArray<
FormControl<number | null>
>
is not assignable to type
FormArray<
AbstractControl<
number | FormControlState<number | null> | null,
number | FormControlState<number | null> | null
>
>
The compiler seems to expect a broader type for the FormControls than what I intended, specifically
number | FormControlState<number | null> | null
instead of simply number | null
. Why is this happening?
While the components compile without errors if I use FormControl
instead of
AbstractControl</code, I prefer not to restrict the <code>FormArray
to be 'flat'. Another workaround involves pulling out the ControlValue
type like this:
export class InnerComponent<ControlValue, C extends AbstractControl<ControlValue>> {
@Input({ required: true }) public formArray!: FormArray<C>
}
However, I find this solution less than ideal as the type of a single control within the InnerComponent
becomes
AbstractControl<any, any> | C
, necessitating type casts if it needs to be specifically of type C
.
You can view the full code snippet in the Angular Playground:
import {Component, Input} from '@angular/core';
import {FormArray, AbstractControl, FormControl} from '@angular/forms';
import {bootstrapApplication} from '@angular/platform-browser';
@Component({
selector: 'inner',
template: '',
standalone: true
})
export class InnerComponent<ControlValue> {
@Input({ required: true }) public formArray!: FormArray<AbstractControl<ControlValue>>
}
@Component({
imports: [InnerComponent],
selector: 'app-root',
standalone: true,
template: `
<inner [formArray]="formArray"></inner>
`,
})
export class PlaygroundComponent {
public formArray!: FormArray<FormControl<number | null>>
}
bootstrapApplication(PlaygroundComponent);