TypeScript:强制转换HTMLElement

TypeScript: casting HTMLElement

有人知道如何在TypeScript中进行转换吗?

我正在尝试这样做:

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

但这给了我一个错误:

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

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


TypeScript使用'<>'包围强制转换,因此以上内容变为:

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

但是,不幸的是,您不能执行以下操作:

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

你得到错误

1
Cannot convert 'NodeList' to 'HTMLScriptElement[]'

但是你可以做:

1
(<HTMLScriptElement[]>document.getElementsByName(id))[0];


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

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

1
2
3
// No type assertions needed
var script: HTMLScriptElement = document.getElementsByTagName('script')[0];
alert(script.type);


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

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


不要键入强制类型转换。决不。使用防护罩:

1
2
3
4
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中缺少的类),它将对您有很大帮助。


最后以:

  • 实际的Array对象(不是NodeList打扮成Array的对象)
  • 保证仅包含HTMLElements而不是强制广播到HTMLElementNode的列表
  • 做正确的事的温暖模糊的感觉

尝试这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
        }
    }
}

请享用。


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

Cannot convert 'NodeList' to 'HTMLScriptElement[]'

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

因此,如果确实在类型系统中将其转换为HTMLScriptElement[],则尝试在编译时调用Array.prototype成员时不会出现类型错误,但是在运行时会失败。


更新的示例:

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

说明文件:

TypeScript-基本类型-类型断言


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

1
2
3
4
5
interface ScriptNodeList extends NodeList {
    [index: number]: HTMLScriptElement;
}

var script = ( <ScriptNodeList>document.getElementsByName('foo') )[0];

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

DOM4还将其指定为正确的返回类型,但是较旧的DOM规范尚不清楚。

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


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

我还将推荐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 also allows you to specify different return types when an
exact string is provided as an argument to a function. For example,
TypeScript’s ambient declaration for the DOM’s createElement method
looks like this:

1
2
3
4
5
6
createElement(tagName: 'a'): HTMLAnchorElement;
createElement(tagName: 'abbr'): HTMLElement;
createElement(tagName: 'address'): HTMLElement;
createElement(tagName: 'area'): HTMLAreaElement;
// ... etc.
createElement(tagName: string): HTMLElement;

This means, in TypeScript, when you call e.g.
document.createElement('video'), TypeScript knows the return value is
an HTMLVideoElement and will be able to ensure you are interacting
correctly with the DOM Video API without any need to type assert.


由于它是NodeList,而不是Array,因此您实际上不应该使用方括号或强制转换为Array。获取第一个节点的属性方法是:

1
document.getElementsByName(id).item(0)

您可以将其转换为:

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

或者,扩展NodeList

1
2
3
4
5
6
interface HTMLScriptElementNodeList extends NodeList
{
    item(index: number): HTMLScriptElement;
}
var scripts = <HTMLScriptElementNodeList> document.getElementsByName('script'),
    script = scripts.item(0);