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

Embed a subcomponent within another component triggered by an onClick event in ReactJS

Watch this quick demo to see my project in action. So, here's the deal - I have a menu with different options, and when "Tickers" is selected, I want it to display the Tabs component. However, if any other menu option is chosen, I don't want the ...

Exploring click event beyond React Component: Jest + Enzyme Testing

I am looking to create a Jest and Enzyme test for a component that includes an event listener. The purpose of the test is to ensure that when a mousedown event occurs outside of a specific HTML element, a change is triggered (for example, toggling a state ...

Error: Unable to access the 'address' property of a null object

I am a beginner in the realm of react and have encountered an issue with my app, which is a simple e-commerce platform. The problem arises when I try to enter the shipping address during the checkout process, as it throws an error. TypeError: Cannot read ...

I keep encountering a parser error when making an AJAX call to retrieve JSON data. It seems to be caused by

here is a snippet of code I am working on $.ajax({ type: 'GET', url: "<%=request.getContextPath()%>/manageUsers.do", cache: false, data:{ "resultType": "json", "submit": $.i18n.prop('esadmin.manage.users ...

Using the setTimeout function with asynchronous tasks

I have a situation where I need to introduce a 5000ms delay before firing an asynchronous function. To accomplish this, I attempted to utilize the setTimeout() method. This async function is called within a loop that runs multiple times, and each time it i ...

"Observed Issue: Ionic2 Array Fails to Update in HTML Display

I am struggling with updating an array in Ionic2 and Angular2. I have tried updating it on the front end but it's not working, even though it updates perfectly on the backend (ts) as confirmed by checking the console. I need assistance with this. Her ...

What's the reason behind the absence of the bar chart in this presentation?

I need help making a bar chart using d3 and Angular(in VSCode). The x-axis doesn't seem to work properly. Can anyone assist me with this issue? Currently, only the y-axis is displaying without any additional response. I've been stuck on this prob ...

The issue with the value of the textarea in Selenium automated tests using

I have a webpage with Javascript where I've implemented a textarea using the following code: var textarea = $("<textarea>"); textarea.change(() => { console.log(textarea.val()); }); When I update the value in the textarea and then chang ...

Guide on optimizing Angular CLI production builds with gzip compression

When I build my angular project using ng build --environment=${environment}, the bundle files created are quite large. The version of "@angular/compiler-cli": "^4.0.0" does not generate .gz files in the dist folder. Is there a simple way to create .gz bu ...

Utilize JavaScript when sharing on social media to avoid the possibility of embedding the entire

This javascript code snippet is extracted from www.twitter.com (simply click to view the source code). I have reformatted it for better readability: if (window.top !== window.self) { document.write = ""; window.top.location = window.self.location; s ...

Exploring the contrast between Vuex store WATCH and SUBSCRIBE

Can you explain the main distinction between watch and subscribe, and when it is most appropriate to use one over the other? According to information on the Vuex official documentation, both methods appear to be essentially identical in functionality and p ...

Once the page is refreshed, the checkbox should remain in its current state and

I have a challenge with disabling all checkboxes on my page using Angular-Js and JQuery. After clicking on a checkbox, I want to disable all checkboxes but preserve their state after reloading the page. Here is an example of the code snippet: $('# ...

Efficient Ways to pass information to an Object within a nested function

const http = require('https'); exports.ip = async (req, res) => { const ip = req.body.ip; const ip_list = ip.trim().split(' '); const count = ip_list.length; var execution_count = 0; var success = {}; // **Creati ...

The type 'GetServerSidePropsContext<ParsedUrlQuery, PreviewData>' does not include property X

My current setup includes: type Session = { bearer: string, firstName: string, lastName: string, etc... }; interface ServerContext extends GetServerSidePropsContext { session: Session, }; export type ServerProps<P extends { [key: string]: ...

Utilizing Angular 2's Routerlink with *ngIf and Parameters

Currently, I am facing an issue with a routerlink that includes a parameter: http://localhost:4200/item/1 I am trying to figure out how to implement an *ngIf statement with a parameter.... Here is what I have attempted so far: <div *ngIf="router.url ...

The margin of the parent container is influencing the margin of the child element

Purple Working on displaying a rectangle shape in a browser using divs in my React project. Everything works fine, but when I add margin to the parent base and then draw the boxes, there's some space between the mouse cursor and the actual box. The ...

Incorporate a file into all API endpoints with Next.js API functionality

Is there a way to incorporate a "bootstrap" file (a file with side-effects) as the first file included in all Next.js APIs? The main issue is that I have a Winston logger in a file that needs to be added to every API endpoint, but this process hinders dev ...

I'm confused why this particular method within a class is not being inherited by the next class. Rather than seeing the expected extension, I am presented with - [Function (

Working fine with the Person class, the register() function displays the correct return statement when logged in the console. However, upon extending it to the Employee class, instead of the expected return statement, the console logs show [Function (anon ...

Allow web applications in Apache ServiceMix to communicate across different domains by enabling Cross

My current project involves deploying an angular2 webapp in servicemix as a war file. As a result, the app runs on the localhost:8181/angular2webapp URL. Additionally, I have a bundle installed for handling REST requests, which essentially functions as a c ...

Guide to configuring an Angular Material Footer using Flex-Layout

Could someone help me with setting up the footer in my Angular Material app? I want it to: stick to the bottom when the content height is smaller than the view-port move down or get pushed down when the content height exceeds the view-port One important ...