Angular 14 captures typed form data as `<any>` instead of the actual data types

On the latest version of Angular (version 14), I'm experiencing issues with strictly typed reactive forms that are not functioning as expected.

The form initialization takes place within the ngOnInit using the injected FormBuilder.

 public form!: FormGroup;

 constructor(private formBuilder: FormBuilder) {}

 ngOnInit(): void {
    this.initializeForm();
  }

  private initializeForm(): void {
    this.form = this.formBuilder.group({
      title: ['', [Validators.required, Validators.minLength(3)]],
      content: ['', Validators.required],
    });
  }

However, when attempting to access the form controls, there is no autocomplete and the type is seen as FormGroup<any>. Furthermore, no error is thrown when trying to access controls that do not exist on the FormGroup object.

  • For example:

https://i.stack.imgur.com/XfR0h.png

  • package.json

https://i.stack.imgur.com/27RsH.png

  • Refer to the Angular official documentation for Typed Forms:

https://angular.io/guide/typed-forms

Answer №1

TL;DR: TypeScript mainly uses interfaces and context to identify its "types." To address the issue, I suggest explicitly creating an interface and passing it to the form since inference is not possible.


In most scenarios, Angular can infer the form type when initialized with correct types in declaration. This response addresses that specific case.

Note on inferred types: Inferred types require more effort for refactoring. Interfaces can easily denote object and attribute types just by looking at their declaration, which cannot be done with inferred types. From my experience, I recommend always using interfaces over inferred types.


Your scenario:

In the example provided, there's no way to deduce the type of the form; you must specify it explicitly. I have created a working example of a typed form. The official documentation mentioned in your question already covers most of the solution.

To get rid of the <any> in your autocomplete, implement your own interface:

export interface IMainForm {
    title: FormControl<string>;
    content: FormControl<string>;
}
public form!: FormGroup<IMainForm>; // <--- Use your interface

Moreover, your usage of this.formBuilder.group has been deprecated because it lacks type safety. You should use the overload with AbstractControlOptions instead (not the array).

private initializeForm(): void {
  this.form = this.formBuilder.group({
    title: new FormControl<string|null>(null, [Validators.required, Validators.minLength(3)]),
    content: new FormControl<string|null>(null, Validators.required)
  });
}

With these adjustments, you will see the typed value as

{Partial<{ title: string; content: string; }>}
. https://i.stack.imgur.com/ghJIw.png


You can refer to type inference details on typescriptlang. Here's a brief excerpt:

Simple type let x = 4, inferred as number

The type of the x variable is determined to be number. This type of inference occurs during variable initialization, setting parameter defaults, and establishing function return types.

Best common type let x = [0, 1, null], resulting in (number | null)[]

For inferring the type of x in the given example, each element in the array's type must be considered. We have two options for the array type: number and null. The best common type algorithm evaluates all candidate types and selects one compatible with all other candidates.

Contextual types

window.onmousedown = function (mouseEvent) { ... }
, yielding MouseEvent

Contextual typing applies to various situations such as function call arguments, assignment right-hand sides, type assertions, object and array literal members, and return statements. The contextual type also contributes as a candidate type in determining the best common type.

Answer №2

If you want to avoid defining an interface, here's a solution that utilizes the inferred type:

form: ReturnType<typeof this.initializeForm>
...
private initializeForm() {
  return this.formBuilder.group({
    title: ['', [Validators.required, Validators.minLength(3)]],
    content: ['', Validators.required],
  });
}

Answer №3

The documentation for Angular lacks clarity in explaining why type checking may not be functioning as expected in your strictly typed Reactive Form. One key reason for this issue is the improper initialization of the form within the OnInit hook.

To resolve this, it is essential to initialize the form directly to allow for proper type inference. Failure to do so results in the form defaulting to FormGroup<any>, thereby negating the benefits of Angular's typed forms.

To address this problem, consider refactoring your code as follows:

@Component({
  selector: 'example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.css']
})
export class ExampleComponent {

   public form = this.formBuilder.group({
      title: ['', [Validators.required, Validators.minLength(3)]],
      content: ['', Validators.required],
  });

  constructor(private formBuilder: FormBuilder) {}
}

I have provided an operational example demonstrating the correct approach. Additionally, I've included a commented-out line attempting to set the wrong type for reference. Uncommenting it will trigger a type error flagged by the editor.

Please note that while @luiscla27's accepted answer appears in this thread, it may mislead individuals encountering similar issues due to overlooking the crucial step of directly initializing their typed reactive form.

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

When trying to use TypeScript with next.js, encountering an unexpected token `?` error is not

Having an issue with next.js, the command npm run dev keeps failing due to a syntax error related to an optional property in a tsx file: Syntax error: Unexpected token 44 | 45 | type State<T_HT> = { > 46 | ghostHighlight: ?{ | ...

Assets failing to duplicate during ng build in production

Currently, I'm developing an application using Angular 4. I recently added a new SVG image to the assets/images folder and made the necessary changes in the angular-cli.json file as well. When I run 'ng build' locally, it successfully copies ...

I am puzzled by this error in Typescript: "Why does the element have an 'any' type when the Object type lacks an index signature?"

Looking to extract an array of keys from an object with nested properties, my current code: public static getKeys(obj: Object) { let keys: string[] = []; for (let k in obj) { if (typeof obj[k] == "Object" && obj[k] !== null) { ...

Exploring alternatives for navigation in nebular without using the global spinner

I am currently working on customizing the nebular ngrx-admin template. There is a global spinner that shows up when navigating from the header to a new page (component). I want to hide this spinner, specifically for certain components where it's not n ...

Utilizing Custom Validators in Angular to Enhance Accessibility

I'm struggling to access my service to perform validator checks, but all I'm getting is a console filled with errors. I believe it's just a syntax issue that's tripping me up. Validator: import { DataService } from './services/da ...

How can a nullable variable be converted into an interface in TypeScript?

Encountered an issue while working on a vue3.x typescript project. The vue file structure is as follows: <template> <Comp ref="compRef" /> </template> <script lang="ts" setup> import {ref} from "vue& ...

The functionality of data binding becomes unclear when the ngif directive is applied to a mat-selection-list in

I am facing an issue with displaying a mat-selection-list based on a condition. Since adding the ngif condition, the data is consistently being set to undefined. I am struggling to identify the root cause of this problem. Thank you in advance for your assi ...

Enhance your Angular application with lazy loading and nested children components using named outlets

Let me start by explaining that the example provided below is a simplified version of my routes that are not functioning properly. I am working on an angular project, specifically a nativescript angular project, and I suspect the issue lies within Angular ...

ESLint has issued a warning indicating that the selector must be utilized as an element

Running Angular 12 and ESLint together has raised some issues for me. Whenever I run ng lint, ESLint reports a problem with the selector below. 10:13 error The selector should be used as an element (https://angular.io/guide/styleguide#style-05-03) @an ...

Is there a way to assign a value to an Angular-specific variable using PHP?

In the development of my Angular 4 application, I encountered an issue while receiving JSON data based on an id value through a PHP script. Upon examining the code, it seems that there should be a value passed into this.PropertiesList. examineProperties(i ...

Is the 'name' field empty in PHP contact form emails being sent to the email address?

Sorry, I'm new to filling out this type of form! I managed to create a contact form page that sends me the 'email address' and 'message' from the form fields, but it's not sending the value entered in the name field, it remai ...

Angular2 fire fails because the namespace 'firebase' does not export the member 'Promise'

I recently set up Angular 2 Fire on my project. "angularfire2": "^5.0.0-rc.0", Now, in my root module (app module), I have the following setup: export const firebaseConfig = { apiKey: "mykey", authDomain: "....", databaseURL: "...", projectId: ...

What is the best way to display inner HTML in an Angular MatAutoComplete MatOption when a user makes a selection?

My challenge lies in displaying a list of properties that contain HTML tags for superscript or subscript in an Angular Material MatAutoComplete list. While I can successfully showcase these values, the issue arises when trying to display a user's sele ...

Troubleshooting CORS issue when using POST method in Spring Boot

I currently have a setup with a Spring Boot application serving as the backend with a rest controller, and an Angular application as the frontend. Both applications are running on localhost with SpringSecurity enabled. Initially, I faced issues when trying ...

What is the process for removing the Angular IDE plugin from Eclipse Oxygen?

After using the free license for 8 days, I found myself needing to continue working. My first attempt at uninstalling Angular-IDE through Eclipse Marketplace Installed screen was unsuccessful. Next, I tried removing Webclipse, but this also did not ...

Passing an event between components in AngularDart

Two components, componentA and componentB, are siblings within the structure of componentMother. When componentA is clicked, an event is triggered and handled by event handlerA. How can this event be passed to componentB and handled by event handler B? W ...

Angular is programmed to detect any alterations

Upon detecting changes, the NgOnChanges function triggers an infinite service call to update the table, a situation that currently perplexes me. Any assistance on this matter would be greatly appreciated. Many thanks. The TableMultiSortComponent functions ...

I often find myself frustrated while using Next.js because the console automatically clears itself, making it difficult for me

I am facing an issue with my form in the Next.js app. Here is how it is defined: <form onSubmit = { async() => await getCertificate(id) .then(resp => resp.json()) .then(data => console.log(data)) }> Whenever there is an erro ...

The ngx-datatable is returning an error message stating that it cannot read the property 'indexes' of an undefined

In my project, I am using ngx-datatable version 15.0.2 and Angular version 8.1.0. Recently, I encountered the following error: ngx-logger.js:852 2019-07-30T15:04:42.930Z ERROR [main.js:4696] TypeError: Cannot read property 'indexes' of unde ...

Define a module function that can be used externally

I am encountering an issue while trying to declare an external module that does not have existing typings. This library has a function that returns a string and takes no arguments. My attempt to define it in a .d.ts file is as follows: declare module "c ...