What is the reason behind the NgForOf directive in Angular not supporting union types?

Within my component, I have defined a property array as follows:

array: number[] | string[] = ['1', '2'];

In the template, I am using ngFor to iterate over the elements of this array:

<div *ngFor="let element of array">
  {{element}}
</div>

However, the compiler is throwing an error:

error TS2322:
Type 'number[] | string[]' is not assignable to
type '(number[] & NgIterable<number>) | null | undefined'.

I am puzzled by Angular inferring that the type of the array property should be

number[] & NgIterable<number>
in this scenario.

I am aware that I can resolve the error by using $any() within the template or by setting

angularCompilerOptions.strictTemplates
to false in tsconfig.json, but I prefer to avoid these solutions unless necessary.

I am hesitant to change the type of the array property to

(number | string)[]</code as it would not accurately reflect my intention of having <code>array
contain only numbers or only strings.

Answer №1

There is a known issue within Angular regarding a bug in the NgFor code. Despite both your code and the NgFor code being correct, there seems to be a mistake in how Angular handles template variable types. This can be demonstrated with the following example:

type NgIterable<T> = T[] | Iterable<T>;

const f = <T, U extends NgIterable<T> = NgIterable<T>>(param: (U & NgIterable<T>) | undefined | null) => false;

const a: string[] | number[] = ['1'];

window.console.info(f(a)); 

When Angular transforms templates into TypeScript, it needs to accurately infer variable types by analyzing AST. In this case, Angular incorrectly infers the type as number[]. This leads to issues when generating "glue" code for the templates.

In some instances, Angular may create a temporary variable or constant with an incorrect type such as

(number[] & NgIterable<number>) | null | undefined
, causing compilation errors like the one shown below:

//...
// templateVar type is "string[] | number[]", it comes from your code
// does not compile
const temp: (number[] & NgIterable<number>) | null | undefined = templateVar; 
ngForInstance.ngForOf = temp;
//...

Answer №2

If you want to convert your array, you can define a getter function

  get convertedArray()
  {
    return this.array as NgIterable<number|string>
  }

Then simply use convertedArray in the *ngFor directive

<div *ngFor="let item of convertedArray">
   {{item}}
</div>

However, some may find this method not very elegant.

Answer №3

Creating a union type for an array in TypeScript can be done using

array: Array<number|string>;
. While union types have not always functioned smoothly in TS, they should work fine with simple types like a|b.

One important thing to note is to avoid naming your array variable array, as it is a reserved keyword and may lead to potential problems down the line.

Answer №4

This particular situation caught my attention, and it can be swiftly resolved using the typing capabilities in typescript 4.9 with the use of satisfies:

array = ['1', '2'] satisfies (number[] | string[]);

As a result, all errors have been successfully addressed.

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

How can I determine which dist folder is utilized during the building of my App if an npm package contains multiple dist folders?

I have integrated an npm package called aurelia-google-maps into my application. This package includes various distribution folders such as AMD, System, CommonJS, Native Modules, and ES2015 within the /node_modules/ directory like so: /node_modules/ /a ...

Retrieve information stored in a component's data variable

After creating a Vue repository using vue init webpack my-app My main.js file looks like this -- // The Vue build version to load with the import command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue fro ...

Passing a value from an HTML template to a method within an Angular 4 component

Encountering an issue with Angular 4. HTML template markup includes a button: <td><a class="btn btn-danger" (click)="delete()"><i class="fa fa-trash"></i></a></td> Data is assigned to each td from a *ngFor, like {{ da ...

Tips for aligning and emphasizing HTML content with audio stimuli

I am looking to add a unique feature where the text highlights as audio plays on a website. Similar to what we see on television, I would like the text to continue highlighting as the audio progresses. Could someone please provide me with guidance on how ...

Ensuring the Presence of a Legitimate Instance in NestJS

I have been working on validating my request with the Product entity DTO. Everything seems to be in order, except for the 'From' and 'To' fields. The validation works correctly for the Customer and Type fields, but when incorrect data i ...

Is it possible to combine TypeScript modules into a single JavaScript file?

Hey there, I'm feeling completely lost with this. I've just started diving into Typescript with Grunt JS and I could really use some assistance. I already have a Grunt file set up that runs my TS files through an uglify process for preparing the ...

extract specific data from JSON using JavaScript

Dealing with JSON data can be tricky, especially when working with multiple sets of information to choose from. When I inspect the data in my JavaScript code using console.log(contacts.data["all"]);, I can see the returned objects clearly. Here's a ...

Angular - Enhance User Experience with Persistent Autocomplete Suggestions Displayed

Is there a way to keep the autocomplete panel constantly enabled without needing to specifically focus on the input field? I attempted to set autofocus on the input, but found it to be clunky and the panel could still disappear if I hit escape. I also ...

Issue with Angular 2 NgFor Pattern Error Message Display Absence

I am attempting to incorporate inputs with a regex requirement within an ngFor loop, but I am not receiving the expected error message when entering something that does not match the required pattern. Even when I input an incorrect pattern, "Test" remains ...

The PHP blocking code in Zend Server not only blocks the response of the current ajax call but also impacts the handling

I encountered a peculiar problem. Suppose I have an ajax call like this; $.ajax({ url:"url1.php", }) Following this ajax call, I have another ajax call as follows; $.ajax({ url:"url2.php", success:function(data){console.log(data);} }) ...

What is the best way to make a Modal triggered by a loop embedded within a table function properly on mobile devices?

While the modal is functioning properly on desktop, it seems to be encountering issues on mobile devices. The modal appears behind the background and is confined by the boundaries of the table (tbody). Below is the code snippet for triggering the modal: ...

Can the renderWith function of a column in angular-datatables retrieve a value from a promise?

Can I trigger a call to a service or filter that retrieves a promise from the renderWith() function of a column? When I attempt this, the result always appears as "[object Object]". vm.dtInstance = {}; vm.dtOptions = DTOptionsBuilder.fromFnPromise(MySe ...

The formBuilder validator pattern seems to be malfunctioning

I am attempting to display a message when the password does not meet the formGroup pattern. Here is how my FormGroup is initialized: this.signupForm = fb.group({ userName: ['', Validators.compose([Validators.required,Validators.pattern(/^&bsol ...

How does the Cluster module in Node.js compare to the Cluster module in Learnboost?

Node.js features its own Cluster core module (source: http://nodejs.org/docs/v0.8.3/api/cluster.html) while Learnboost has introduced a similarly named Cluster module as well (source: , https://github.com/LearnBoost/cluster). What are the differences betw ...

Navigate in Angular to the server but add token headers using an interceptor

I need to incorporate authenticated file serving from the backend into my Angular routing: { path: 'files/:id/:name', pathMatch: 'full', ??? } My HTTP interceptor is already set up to include token headers in other requests. What I wan ...

Problem concerning the window object in a React functional component

Hey there, I am currently facing a situation where I need to access the window object within my React component in order to retrieve some information from the query string. Here is an excerpt of what my component code looks like: export function MyCompone ...

Leveraging Selenium to dismiss a browser pop-up

While scraping data from Investing.com, I encountered a pop-up on the website. Despite searching for a clickable button within the elements, I couldn't locate anything suitable. On the element page, all I could find related to the 'X' to cl ...

How can an object inside an array be destructured in just one line?

Consider the following array: const array = [{b:2}] Can you extract the value of b using destructuring in just one line? I attempted something similar to this approach, but it did not yield the desired result: const [{b} = array] ...

Is it possible for me to access information from an external URL using JSON?

As I delve into learning about JSON for app development, I've encountered an issue with a JSON and PHP-based chat system. While the code functions properly for the same origin policy, when it comes to sending and receiving data from an external URL, i ...

Sharing markdown content between two Vue.js components

I have a markdown editor in View A which is displaying the result in the current View. My goal is to share this result with another page, View B. In View A, there is a button that allows the user to share the markdown result with View B. I am using a texta ...