【问题标题】:typescript for loop over HTMLCollection用于循环 HTMLCollection 的打字稿
【发布时间】:2019-04-05 12:39:52
【问题描述】:

我想遍历结果

document.getElementsByTagName("...");

它返回一个HTMLCollection,而不是一个数组。所以我不能简单地使用forEach。 以下是可能的,但看起来不太好:

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

对于 javascript,几乎存在完全相同的问题: For loop for HTMLCollection elements

显然,随着最新更新,现代浏览器支持:

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

但是打字稿编译器抱怨:

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

尽管如此,它仍然可以转换为正确的 Javascript。它甚至知道我在这里做什么,而不仅仅是复制源代码。编译后的输出是这样的:

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

这很好,因为即使在旧浏览器上也支持生成的代码。但我想摆脱错误信息。

我的编译器选项是这样的:

{
    "compilerOptions": {

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

我确实尝试过编辑我的 lib 选项。这个特定的特性是 ES6 的一部分,并且已经被重新设计了几次。所以我在 lib 中测试了各种 ecmascript 版本,但找不到有效的设置。

【问题讨论】:

    标签: typescript


    【解决方案1】:

    如果要使用forEach,可以将其转换为数组

    const elements: Element[] = Array.from(document.getElementsByTagName("..."));
    elements.forEach((el: Element) => {
        // do something
    })
    

    【讨论】:

      【解决方案2】:

      打字稿编译器在指定一个额外的标志downlevelIteration后支持这一点:

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

      但是,这不仅会消除错误,还会更改编译器输出。 这个输入打字稿:

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

      编译成这个javascript:

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

      【讨论】:

      • 将目标设置为 es5 以使“for of”循环与 HTMLCollection 对象一起工作并不理想。只需使用 for 循环。仅当您想支持 IE 等旧浏览器时才设置为 es5。使用 ES2015。更小的包大小。
      • 你错了,必要的编译器设置是下级迭代。它与 es5 无关(javascript 版本是项目的约束)。事实上,令人印象深刻的是 tsc 能够发出兼容的语法,一直到 es5。
      【解决方案3】:

      要消除错误消息,您可以将列表添加为 any 类型。 Typescript 与 JavaScript 的区别在于 Type。因此,最好将类型添加到变量中。供您参考,我添加了 any 类型:

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

      其实,这是一种典型的“上下文打字”。当表达式的类型由其位置隐含时,就会发生上下文类型。对于上面给出类型错误的代码,TypeScript 类型检查器使用 document.getElementsByClassName("events") 的类型来推断 ForLoop 函数的类型。如果此函数表达式不在上下文键入位置,则 list 将键入 any,并且不会发出错误。

      如果上下文类型表达式包含显式类型 信息,上下文类型被忽略。

      上下文类型适用于许多情况。常见情况包括 函数调用的参数、赋值的右侧、类型 断言、对象和数组字面量的成员以及返回 陈述。上下文类型也最好作为候选类型 通用类型。[来自 TypeScript 文档]

      【讨论】:

        【解决方案4】:

        示例用法:

        const inputs = document.getElementsByTagName("input");
        for (let index = 0; index < inputs.length; index++) {
          const input = inputs.item(index);
        }
        

        【讨论】:

        • 你读过这个问题吗?这与第二个代码示例几乎完全相同。
        • 几乎!。我的答案使用了 HTMLCollection (developer.mozilla.org/en-US/docs/Web/API/HTMLCollection/item) 公开的函数 item(index)。
        • @DanKodi:OP 知道这个解决方案有效,但不认为它看起来很好。添加 .item 可能不会改变这种感觉。 :)
        【解决方案5】:

        我知道这是对这个问题的一个非常晚的答案,但认为在线程中可能会很好......在一个 Angular 项目中,我正在寻找一种使用数组解构的方法(谁不只是喜欢HTMLCollectionOf&lt;T&gt; 的数组解构 amiright?),如下例所示:

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

        但是会报错:

        'Type 'HTMLCollectionOf' 必须有一个 'Symbol.iterator' 返回 iterator.ts(2488)'的方法

        另外,我遇​​到过迭代集合会很好的场景,例如:

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

        我发现添加了一个 global-types-extensions.d.ts 文件,内容如下:

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

        ...修复了我从 TS 收到的错误/警告。

        所以现在,我可以愉快地绕过数组解构并在任何 HTMLCollectionOf 上使用 for...of ??。

        【讨论】:

        • 呃,你有没有看过接受的答案?这个问题充斥着奇怪的解决方法,而存在一个非常干净的解决方案。只需在编译器标志中指定它。
        • 我确实查看了接受的答案并尝试对 .tsconfig 文件实施建议的更改;但是,这并没有解决我的问题。也许,这是因为 Angular 现在默认以 es2015 为目标。
        • 您添加的声明应该与正确配置的 tsconfig 添加的声明几乎相同。你确定你做得对吗?例如,您正在写.tsconfig,这实际上是您使用的名称吗?应该是tsconfig.json
        • 你是对的!我指的是tsconfig.json 文件,只是在我之前的评论中将此文件称为“.tsconfig”时犯了一个错误,现在看来我无法编辑了。根据您的回答,我开始寻找建议的解决方案似乎对我不起作用的可能原因;我发现将DOM.Iterable 添加到lib 数组对我来说很重要。事实证明,当定位ES2015 时,您甚至不需要在compilerOptions 中指定"downlevelIteration": true。感谢您提供有关正确配置的 tsconfig 的提示! ?
        猜你喜欢
        • 2020-06-04
        • 2017-04-21
        • 2014-05-10
        • 2020-06-10
        • 2019-09-24
        • 2018-09-08
        • 1970-01-01
        相关资源
        最近更新 更多