Using typescript to iterate through an HTMLCollection with a for loop

Looking to loop through the results from

document.getElementsByTagName("...");

This method returns an HTMLCollection, not an array. Therefore, using forEach directly is not possible. One way to achieve this is as follows:

let elements = document.getElementsByTagName("...");
for (var i = 0, m = elements.length; i < m; i++) {
    let element = elements[i];
}

A similar question exists for javascript: For loop for HTMLCollection elements

Recent updates show that modern browsers now support:

var list = document.getElementsByClassName("events");
for (let item of list) {
    log(item.id);
}

However, there seems to be a typescript compiler error:

error TS2495: Type 'NodeListOf<HTMLParagraphElement>' is not an array type or a string type.

Despite this, the code transpiles to valid Javascript with proper functionality. The compiled output looks like this:

var elements = main_div.getElementsByTagName("p");
for (var _i = 0, elements_1 = elements; _i < elements_1.length; _i++) {
    var element = elements_1[_i];
}

It's reassuring to see that the generated code remains compatible even with older browsers. Yet, it would be ideal to resolve the error message.

The compilerOptions set are as follows:

{
    "compilerOptions": {

        "module": "commonjs",
        "target": "es5",
        "sourceMap": true,
        "rootDir": "src",
        "lib": [
            "es2015",
            "dom"
        ]
    }
}

Attempts were made to modify the lib options in search of a solution, considering that the feature originated in ES6 and underwent revisions. Testing different ecmascript versions did not yield a successful configuration.

Answer №1

If you'd like to utilize forEach, simply convert it into an array first

const items: Item[] = Array.from(document.querySelectorAll("..."));
items.forEach((item: Item) => {
    // perform a task
})

Answer №2

To enable support for iterating through elements, the typescript compiler requires the use of the downlevelIteration flag:

{
    "compilerOptions": {
        "target": "es5",
        "downlevelIteration": true
    }
}

Enabling this flag not only resolves any errors but also affects the output generated by the compiler. For example, consider the following TypeScript code:

function compileTest(){
  let elements = document.getElementsByTagName("p");
  for(let element of elements){
    console.log(element);
  }
}

After compilation, the TypeScript code is transformed into JavaScript as shown below:

function compileTest() {
    var e_1, _a;
    var elements = document.getElementsByTagName("p");
    try {
        for (var elements_1 = __values(elements), elements_1_1 = elements_1.next(); !elements_1_1.done; elements_1_1 = elements_1.next()) {
            var element = elements_1_1.value;
            console.log(element);
        }
    }
    catch (e_1_1) { e_1 = { error: e_1_1 }; }
    finally {
        try {
            if (elements_1_1 && !elements_1_1.done && (_a = elements_1.return)) _a.call(elements_1);
        }
        finally { if (e_1) throw e_1.error; }
    }
}

Answer №3

Here is an example of how this code can be used:

const formInputs = document.getElementsByTagName("input");
for (let i = 0; i < formInputs.length; i++) {
  const inputField = formInputs.item(i);
}

Answer №4

If you want to eliminate the error message, one solution is to assign the list as a type of any. TypeScript distinguishes itself from JavaScript through its strong typing system. Therefore, it is recommended to specify the type for your variables. Below is an example with the any type added:

var list: any = document.getElementsByClassName("events");
for (let item of list) {
    console.log(item.id);
}

This concept is known as "contextual typing". Contextual typing occurs when the type of an expression is inferred based on its context. In the code snippet above, the TypeScript type checker used the type of document.getElementsByClassName("events") to determine the type of the ForLoop function in order to highlight the error. If this function were not contextually typed, the list variable would default to type any and no error would be triggered.

If there is explicit type information within a contextually typed expression, the contextual type takes precedence over it.

Contextual typing is applicable in various scenarios such as function arguments, assignment right hand sides, type assertions, object and array literals, and return statements. The contextual type also serves as a candidate type in the best common type resolution.[From TypeScript Document]

Answer №5

Although this response may come a bit late, I wanted to share my insights on how to handle array destructuring in an Angular project. In one of my projects, I encountered the need to use array destructuring with HTMLCollectionOf<T>, as demonstrated below:

const [zoomControl] = document.getElementsByClassName('leaflet-control-zoom');

However, I faced an error stating:

'Type 'HTMLCollectionOf' must have a 'Symbol.iterator' method that returns an iterator.ts(2488)'

There were also instances where iterating over collections proved to be beneficial, such as:

const elementsWithClassName = document.getElementsByClassName('class-name');
for(const a of elementsWithClassName) {
  console.log(a)
}

To resolve these issues, I created a global-types-extensions.d.ts file with the following code snippet:

declare global {
  interface HTMLCollectionOf<T extends Element> {
    [Symbol.iterator](): Iterator<T>;
  }
}
export {};

Implementing this solution effectively addressed the error and warning messages I encountered in TypeScript.

Now, I can freely utilize array destructuring and for...of loops with any HTMLCollectionOf. 😎👍.

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

Tips for bringing in an enum from TypeScript?

I am working with a module defined in TypeScript that looks like this: declare module MyTypes { export enum MyEnum { GOOD = 'Good', BAD = 'Bad', UNKNOWN = '-' } export interface MyType1 { ...

Guide on calculating the total of an object's attributes in Angular

Given a JSON object structured as follows: { "baskets": [{ "id": 127, "name": "name 1", "kpIs": [{ "id": 419, "name": var a1, "incentive": 0, "ta ...

The 'mergeMap' property is not found on the 'Observable<any>' type

Currently, I'm working on an HttpInterceptor within my Ionic 4 application. My goal is to retrieve the Bearer Authorization token stored in local storage. Although I attempted to utilize mergeMap for this task, I kept encountering the following error ...

What is the process for testing ng-select in Angular using the testing library?

Below is a list of companies displayed in an ng-select: <div class="input-group"> <ng-select role="company" id="company" class="form-control" #Company formControlName="company"&g ...

Is it possible to use the inheritance type for a function parameter derived from an object?

When working with Angular, I have been using TypeScript for some time now. Previously, I used to set function parameter types in a hardcoded way, as shown below: login(email: string) {} However, I recently tried to inherit from another object and changed ...

"`status.map is not a function" error thrown in TypeScript when using useState and Map`

I'm a bit confused about the Error I'm encountering in TypeScript. Whenever I input a letter, I get an error popup saying "TypeError: status.map is not a function". This is my first time working with TS and I'm trying to figure out what&apos ...

Tips for effectively transferring data between components in Angular 2

One of the challenges I'm facing is with my header component. It has a function that toggles a class on click, and it works perfectly within the header component. However, I now want to extend this functionality to my nav component in order to add cla ...

Nest Js file uploads encountering issues when used in conjunction with JavaScript FormData functionality

I'm encountering some difficulties in parsing a request sent from the front-end using FormData. Below is an example request generated from Postman for Axios in node.js. Interestingly, when I use the same request in the Postman app, it functions as int ...

Issue with Props Type Check not functioning as expected in Typescript with React

I am utilizing Typescript within my React application. I aim to rigorously verify the type of props being passed to my components and trigger an error if it does not match. import React from "react"; import styles from "./ServiceDetailCard.css"; type Ser ...

Navigating through nested objects using Rxjs

How to Extract Specific Attribute Values from Nested Objects Array using RxJS const obj = { name: 'campus', buildings: [ { name: 'building', floors: [ { name: 'floo ...

Can you please provide information on the callback function type used in the filter method of TypeScript?

transactionsData = [ { id: 101, name: 'transaction 1' }, { id: 201, name: 'transaction 2' }, { id: 301, name: 'transaction 3' }, { id: 401, name: 'transaction 4' } ]; constructor( private objGreetingsSe ...

Incompatibility issues between NestJS and socket.io package

After diligently following the documentation, I attempted to install and work with socket.io on nestjs. However, I encountered multiple issues when installing packages. Despite trying different methods to update the package, nothing seemed to resolve the i ...

AngularJS Dilemma: Virtual Machine Data Set but No Rendering in Sight

Below is the AngularJS controller code written in Typescript: /// <reference path='../../definitions.d.ts' /> module baseApp.viewControls.products { export interface IProductsScope extends IAppScope { vm: { product ...

Using Angular i18n to create dynamic forms from an array of objects

After receiving an object from the API, I created a form using an array of objects in TypeScript. Although the form is rendered correctly, it fails to translate when I try to localize the application. import { SpecializedAreasComponents } from 'src/a ...

Using Twitch OAuth with Nebular in Angular

For a friend, I am in the process of developing a custom website using the Nebular package. I want users to log in with their Twitch accounts to access the site, but I am encountering issues with the NbAuthModule. Below is the code snippet: app.module.ts ...

What is the best way to imitate a PubNub event in a Jest test that was added using pubnub.addListener?

Currently, I am working on a package that utilizes PubNub. My goal is to write jest tests for all the package files, but I have encountered an issue. Specifically, I am struggling to figure out how to cover events within the listener. // add listener ...

Transforming a recursive array or object into a flattened object type using TypeScript

I am currently working on creating a function that can handle a recursive object/array structure where each node contains a "name" and optionally, "children". My goal is to have a function that transforms this recursive structure into a type-safe object. T ...

Varieties of types within class structures

I've been delving into the world of implementing generic types in classes, but it seems that my understanding of what can and cannot be done with generics is a bit lacking. Hopefully someone out there can shed some light on this for me! My Objective ...

Issue encountered in ../../../../ Unable to locate namespace 'Sizzle'

Following the execution of npm install @types/jquery, I encountered a compilation issue while running my Angular project with ng serve ERROR in ../../../../../../AppData/Roaming/JetBrains/WebStorm2020.1/javascript/extLibs/global-types/node_modules/@types/j ...

What is the implementation of booleans within the Promise.all() function?

I am looking to implement a functionality like the following: statusReady: boolean = false; jobsReady: boolean = false; ready() { return Promise.all([statusReady, jobsReady]); } ...with the goal of being able to do this later on: this.ready().then(() ...