TypeScript:强制转换HTMLElement

时间:2012-10-02 08:33:05

标签: typescript

任何人都知道如何使用TypeScript进行投射?

我正在尝试这样做:

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

我无法访问脚本元素的'type'成员,除非我将其转换为正确的类型,但我不知道如何执行此操作。我搜索了文档&样品,但我找不到任何东西。

14 个答案:

答案 0 :(得分:232)

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

答案 1 :(得分:34)

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

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

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

答案 2 :(得分:21)

您总是可以使用以下方式破解类型系统:

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

答案 3 :(得分:16)

不要输入演员表。决不。使用型号防护:

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中缺少的类,例如。

答案 4 :(得分:11)

最终:

  • 一个实际的Array对象(不是NodeList打扮成Array
  • 保证仅包含HTMLElements的列表,而不是Node强制投放到HTMLElement
  • 温暖的模糊感做正确的事

试试这个:

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

享受。

答案 5 :(得分:9)

只是澄清一下,这是正确的。

  

无法将'NodeList'转换为'HTMLScriptElement []'

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

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

答案 6 :(得分:4)

更新示例:

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

文档:

TypeScript - Basic Types - Type assertions

答案 7 :(得分:3)

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

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

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

答案 8 :(得分:1)

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

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

另见http://typescript.codeplex.com/workitem/252

答案 9 :(得分:0)

由于它是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);

答案 10 :(得分: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还允许您在指定时指定不同的返回类型   确切字符串作为函数的参数提供。例如,   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 Video API而无需输入assert。

答案 11 :(得分:0)

$adddata_PRST = $MYPDO->prepare("INSERT INTO tblData (AddDate, Price) VALUES(NOW(), :Price)");

答案 12 :(得分:0)

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

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

如果您将标记名称单独传递给querySelectorquerySelectorAll,由于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

答案 13 :(得分:0)

作为 CertainPerformanceanswer 的扩展,如果您利用 declaration merging 来扩充标准定义库的 Document 接口,您可以为 { 添加通用覆盖{1}} 方法(或与此相关的任何其他方法),参数默认设置为 getElementsByName 以在未明确提供类型参数时模仿非泛型版本的行为:

HTMLElement

然后在用户代码中您可以明确传递所需的类型:

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

Playground


请注意,您需要重新指定 const scripts = document.getElementsByName<HTMLScriptElement>("name"); //NodeListOf<HTMLScriptElement> 列表,因为只能合并相同的声明。