221

我正在尝试这样做:

var script:HTMLScriptElement = document.getElementsByName("script")[0];
alert(script.type);

但它给了我一个错误:

Cannot convert 'Node' to 'HTMLScriptElement': Type 'Node' is missing property 'defer' from type 'HTMLScriptElement'
(elementName: string) => NodeList

除非我将其转换为正确的类型,否则我无法访问脚本元素的“类型”成员,但我不知道该怎么做。我搜索了文档和示例,但找不到任何东西。

4

14 回答 14

283

TypeScript 使用 '<>' 来包围强制转换,所以上面变成:

var script = <HTMLScriptElement>document.getElementsByName("script")[0];

但是,不幸的是,您不能这样做:

var script = (<HTMLScriptElement[]>document.getElementsByName(id))[0];

你得到错误

Cannot convert 'NodeList' to 'HTMLScriptElement[]'

但你可以这样做:

(<HTMLScriptElement[]><any>document.getElementsByName(id))[0];
于 2012-10-02T08:47:36.087 回答
36

从 TypeScript 0.9 开始,该lib.d.ts文件使用专门的重载签名来返回正确的类型以调用getElementsByTagName.

这意味着您不再需要使用类型断言来更改类型:

// No type assertions needed
var script: HTMLScriptElement = document.getElementsByTagName('script')[0];
alert(script.type);
于 2014-01-15T23:48:40.120 回答
36

不要键入 cast。绝不。使用类型保护:

const e = document.getElementsByName("script")[0];
if (!(e instanceof HTMLScriptElement)) 
  throw new Error(`Expected e to be an HTMLScriptElement, was ${e && e.constructor && e.constructor.name || e}`);
// locally TypeScript now types e as an HTMLScriptElement, same as if you casted it.

让编译器为您完成工作,并在您的假设错误时得到错误。

在这种情况下,它可能看起来有点矫枉过正,但如果您稍后再回来更改选择器,它将对您有很大帮助,例如添加 dom 中缺少的类。

于 2017-04-20T17:18:53.560 回答
21

您始终可以使用以下方法破解类型系统:

var script = (<HTMLScriptElement[]><any>document.getElementsByName(id))[0];
于 2012-10-02T19:22:27.400 回答
15

我们可以使用显式的返回类型键入我们的变量:

const script: HTMLScriptElement = document.getElementsByName(id).item(0);

或断言TSX需要):

const script = document.getElementsByName(id).item(0) as HTMLScriptElement;

或者在更简单的情况下,使用尖括号语法断言。


类型断言类似于其他语言中的类型转换,但不执行数据的特殊检查或重组。它没有运行时影响,并且纯粹由编译器使用。

文档:

TypeScript - 基本类型 - 类型断言

于 2018-09-25T09:58:43.443 回答
13

最后:

  • 一个实际的Array对象(不是NodeList装扮成的Array
  • 保证只包含HTMLElements而不是Node强制转换为HTMLElements 的列表
  • 做正确的事的温暖模糊感觉

试试这个:

let nodeList : NodeList = document.getElementsByTagName('script');
let elementList : Array<HTMLElement> = [];

if (nodeList) {
    for (let i = 0; i < nodeList.length; i++) {
        let node : Node = nodeList[i];

        // Make sure it's really an Element
        if (node.nodeType == Node.ELEMENT_NODE) {
            elementList.push(node as HTMLElement);
        }
    }
}

享受。

于 2015-09-25T17:37:46.147 回答
9

只是为了澄清,这是正确的。

无法将“NodeList”转换为“HTMLScriptElement[]”

因为 aNodeList不是一个实际的数组(例如,它不包含.forEach, .slice,.push等...)。

因此,如果它确实HTMLScriptElement[]在类型系统中转换,如果您尝试Array.prototype在编译时调用它的成员,您将不会收到类型错误,但它会在运行时失败。

于 2012-10-02T19:19:03.567 回答
8

与其使用类型断言、类型保护或any解决该问题,更优雅的解决方案是使用泛型来指示您选择的元素的类型。

不幸的是,getElementsByName不是通用的,而是querySelectorquerySelectorAll是。(querySelector并且querySelectorAll也更加灵活,因此在大多数情况下可能更可取。)

如果您将标签名称单独传递给querySelectoror querySelectorAll,由于以下行,它将自动正确输入lib.dom.d.ts

querySelector<K extends keyof HTMLElementTagNameMap>(selectors: K): HTMLElementTagNameMap[K] | null;

例如,要选择页面上的第一个脚本标签,就像您的问题一样,您可以执行以下操作:

const script = document.querySelector('script')!;

就是这样——TypeScript 现在可以推断出它script现在是一个HTMLScriptElement.

querySelector当您需要选择单个元素时使用。如果您需要选择多个元素,请使用querySelectorAll. 例如:

document.querySelectorAll('script')

结果是NodeListOf<HTMLScriptElement>.

如果您需要更复杂的选择器,您可以传递一个类型参数来指示您要选择的元素的类型。例如:

const ageInput = document.querySelector<HTMLInputElement>('form input[name="age"]')!;

结果ageInput被键入为HTMLInputElement.

于 2020-08-11T02:40:36.980 回答
3

这似乎解决了问题,使用[index: TYPE]数组访问类型,干杯。

interface ScriptNodeList extends NodeList {
    [index: number]: HTMLScriptElement;
}

var script = ( <ScriptNodeList>document.getElementsByName('foo') )[0];
于 2012-10-05T08:37:09.460 回答
1

如果 TypeScript 将 HTMLCollection 而不是 NodeList 定义为返回类型,则可以在声明文件 (lib.d.ts) 中解决。

DOM4 也将其指定为正确的返回类型,但旧的 DOM 规范不太清楚。

另请参阅http://typescript.codeplex.com/workitem/252

于 2012-11-08T20:32:58.177 回答
1

我也会推荐sitepen指南

https://www.sitepen.com/blog/2013/12/31/definitive-guide-to-typescript/(见下文)和 https://www.sitepen.com/blog/2014/08/22/advanced -typescript-concepts-classes-types/

TypeScript 还允许您在将确切的字符串作为参数提供给函数时指定不同的返回类型。例如,TypeScript 对 DOM 的 createElement 方法的环境声明如下所示:

createElement(tagName: 'a'): HTMLAnchorElement;
createElement(tagName: 'abbr'): HTMLElement;
createElement(tagName: 'address'): HTMLElement;
createElement(tagName: 'area'): HTMLAreaElement;
// ... etc.
createElement(tagName: string): HTMLElement;

这意味着,在 TypeScript 中,当您调用例如 document.createElement('video') 时,TypeScript 知道返回值是一个 HTMLVideoElement,并且能够确保您与 DOM 视频 API 正确交互,而无需键入 assert。

于 2015-08-25T18:35:18.077 回答
1

作为CertainPerformance答案的扩展,如果您利用声明合并来扩充标准定义库的Document接口,则可以为getElementsByName方法(或任何其他方法)添加一个通用覆盖,并将参数默认设置HTMLElement为模仿未显式提供类型参数时的非泛型版本:

interface Document
  extends Node,
    DocumentAndElementEventHandlers,
    DocumentOrShadowRoot,
    GlobalEventHandlers,
    NonElementParentNode,
    ParentNode,
    XPathEvaluatorBase {
  getElementsByName<T extends HTMLElement>(elementName: string) : NodeListOf<T>;
}

然后在用户代码中,您可以显式传递所需的类型:

const scripts = document.getElementsByName<HTMLScriptElement>("name"); //NodeListOf<HTMLScriptElement>

操场


请注意,您需要重新指定extends列表,因为只能合并相同的声明。

于 2021-05-15T23:42:06.907 回答
0

由于它是 a NodeList,而不是Array,因此您不应该真正使用括号或强制转换为Array. 获取第一个节点的属性方式是:

document.getElementsByName(id).item(0)

你可以这样:

var script = <HTMLScriptElement> document.getElementsByName(id).item(0)

或者,扩展NodeList

interface HTMLScriptElementNodeList extends NodeList
{
    item(index: number): HTMLScriptElement;
}
var scripts = <HTMLScriptElementNodeList> document.getElementsByName('script'),
    script = scripts.item(0);
于 2013-12-19T17:09:31.887 回答
0
var script = (<HTMLScriptElement[]><any>document.getElementsByName(id))[0];    
于 2018-09-02T13:36:56.517 回答