TypeScript mistakenly infers the incorrect type for AbstractControl when used with a generic type_declaration

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

Answer №1

To implement the desired functionality, utilize the following syntax:

<ControlValue extends AbstractControl<any>>

import {Component, Input} from '@angular/core';
import {FormArray, AbstractControl, FormControl} from '@angular/forms';
import {bootstrapApplication} from '@angular/platform-browser';

@Component({
  selector: 'inner',
  template: 'asdasd',
  standalone: true
})
export class InnerComponent<ControlValue extends AbstractControl<any>> {
  @Input({ required: true }) public formArray!: FormArray<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);

Simply replace AbstractControl with FormControl as suggested.

Using FormControl over AbstractControl is recommended since FormControl already includes the functionalities of AbstractControl.

import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { Component, Input } from '@angular/core';
import {
  FormArray,
  AbstractControl,
  FormControl,
  FormsModule,
  ReactiveFormsModule,
} from '@angular/forms';

@Component({
  selector: 'inner',
  template: 'inner',
  standalone: true,
})
export class InnerComponent<ControlValue> {
  @Input({ required: true }) public formArray!: FormArray<
    FormControl<ControlValue>
  >;
}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [InnerComponent, ReactiveFormsModule],
  template: `
    <inner [formArray]="formArray"></inner>
  `,
})
export class App {
  public formArray!: FormArray<FormControl<number | null>>;
  name = 'Angular';
}

bootstrapApplication(App);

View Stackblitz Demo

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Converting an existing array into a TypeScript string literal type: A step-by-step guide

Converting TypeScript Arrays to String Literal Types delves into the creation of a string literal type from an array. The question raised is whether it's feasible to derive a string literal from an existing array. Using the same example: const furnit ...

Having trouble accessing functions in Typescript when importing JavaScript files, although able to access them in HTML

Recently, I started incorporating TypeScript and React into my company's existing JavaScript code base. It has been a bit of a rollercoaster ride, as I'm sure many can relate to. After conquering major obstacles such as setting up webpack correc ...

Instructions for implementing personalized horizontal and vertical scrolling within Angular 9

I am currently working on an angular application where users can upload files, and I display the contents of the file on the user interface. These files may be quite long, so I would need vertical scrolling to navigate through them easily. Additionally, fo ...

From JSON Schema to ng2-tree treemodel: Implementing in Angular 4

I am looking to create a dynamic Tree view of JSON in the UI that can be edited and saved on the fly. Currently, I am experimenting with TreeModel in Angular 4, but I'm facing challenges because the JSON schema and TreeModel schema are different. I ...

What causes the ngIf directive to update the view of an HTTP observable only upon reloading the page?

Presently, I am utilizing a template: <div *ngIf="(currentUser | async)?.can('book')">Book Now</div> accompanied by its component: readonly currentUser: Observable<CurrentUser>; constructor(private userService: UserSe ...

Top method for dynamically generating a recursive treeview from data fetched from an API

I am currently learning Angular 2 and working on creating an expandable tree-view that pulls data from a potentially large third-party API. The underlying structure of the API is structured like this: - Home (id: 1053) - - Rugby League (id: 1054) - - - Su ...

Angular 5 with Typescript encountered a failure in webpack due to the absence of the property "data" on the Response

I am encountering an issue during webpack compilation. It compiles successfully if I remove .data, but then the page crashes with calls from template->component (which in turn calls a service). Here is the error I am facing: ERROR in src/app/compone ...

Is it possible for an Interface's property to be a type that contains an array?

As I dive into the Angular code, I encountered a peculiar type for a property within an Interface named 'Origin' that has left me perplexed. Here's the snippet: export interface Origin { areaNum?: number; open?: { [key: stri ...

Despite the presence of ngIf, the tab is still displayed

Even though ngIf is used, the FirstTab remains visible. How can I completely remove the tab? Here is the code snippet: <tabs style="min-height:auto"> <tab tabTitle="FirstTab" *ngIf="str=='dp'" > <first-tab> ...

Bluemix is equipped with a robust build pipeline that allows users to easily set their

I'm in the process of deploying an angular 2 app on Bluemix, with the code stored on GitHub. My goal is to have the app deploy automatically whenever I push changes, so I've set up a pipeline for this purpose. Initially, I started with the build ...

Unable to retrieve data from database using Angular 4

I am currently working with Angular 4 and using a MySQL database. This is my service: service.ts getData(){ return this.http.get('http://example/users.php').map(res=>{ return res.json(); }).catch(err=>{ return err.jso ...

Step-by-step guide on crafting a personalized button component in Angular 2 and above with the help of ControlValueAccessor

Are you looking to create a custom button component in Angular versions 2 and above using ControlValueAccessor? Do you need help handling click button and focus events within your custom button? Check out the following sample code: import { Component, OnI ...

Using ngFor to dynamically populate drop-down options from a key value object in Angular 2

How can I use ngFor in Angular 2 to dynamically populate options in dropdown menus from a key value object? Below is the JSON data: { "employment": { "0": "Employed", "2": "Un-employed", "3": "Retired", "4": "Self" "V ...

The node controller function is receiving an undefined object value

Recently, I built a contact form using Angular 7 and connected it with Nodemailer to send the form details to a specified email upon submission. While the frontend view functions properly and sends values correctly, I encountered an issue on the backend wh ...

Exporting Axios.create in Typescript can be accomplished by following a few simple

My code was initially working fine: export default axios.create({ baseURL: 'sample', headers: { 'Content-Type': 'application/json', }, transformRequest: [ (data) => { return JSON.stringify(data); } ...

Issue with CanActivateChild not being processed

After logging out and navigating to /, I am facing an issue where the canActivateChild guards are not being executed to redirect to the login page. The main requirement is that none of the application should be accessible without first logging in. Below ...

Invalid Argument: Cannot use an empty value with the AsyncPipe at invalidArgumentError

I'm facing an issue with extracting a string value from the Observable using the pipe and map operators. Despite my efforts, I always end up with an empty string as the result. I'm hoping that someone can assist me in understanding the cause of t ...

"Uh-oh! Debug Failure: The statement is incorrect - there was a problem generating the output" encountered while attempting to Import a Custom Declarations File in an Angular

I am struggling with incorporating an old JavaScript file into my Angular service. Despite creating a declaration file named oldstuff.d.ts, I am unable to successfully include the necessary code. The import statement in my Angular service seems to be worki ...

Angular6 table using *ngFor directive to display objects within an object

Dealing with Angular 6 tables and encountering a challenge with an item in the *ngFor loop. Here is my HTML view: <table class="table table-bordered text-center"> <tr> <th class="text-center">Cuenta</th> <th class="te ...

Steps for creating an Observable<Object[]> using data from 2 different API requests

My goal is to retrieve an Observable<MyResult[]> by making 2 separate API calls to load the necessary data. The first call is to load MyItem. The second call is to load Gizmos[] for each item. In a previous question, I loaded the second API into t ...