如何简洁地断言一个对象具有特定的属性?

时间:2019-12-01 00:52:32

标签: typescript

我要将ASP.NET WebForms的Focus.js(嵌入在System.Web.dll中)转换为TypeScript(因为我正在维护一个WebForms项目,该项目大量使用了ASP.NET WebForms的库存)客户端脚本)。

这是WebForm_IsInVisibleContainer中的原始JavaScript函数Focus.js

function WebForm_IsInVisibleContainer(ctrl) {
    var current = ctrl;
    while((typeof(current) != "undefined") && (current != null)) {
        if (current.disabled ||
            ( typeof(current.style) != "undefined" &&
            ( ( typeof(current.style.display) != "undefined" &&
                current.style.display == "none") ||
                ( typeof(current.style.visibility) != "undefined" &&
                current.style.visibility == "hidden") ) ) ) {
            return false;
        }
        if (typeof(current.parentNode) != "undefined" &&
                current.parentNode != null &&
                current.parentNode != current &&
                current.parentNode.tagName.toLowerCase() != "body") {
            current = current.parentNode;
        }
        else {
            return true;
        }
    }
    return true;
}

我已将其注释到此TypeScript中:

function WebForm_IsInVisibleContainer( ctrl: HTMLElement ): boolean {
    var current = ctrl;
    while( ( typeof ( current ) != "undefined" ) && ( current != null ) ) {
        if( current.disabled ||
            ( typeof ( current.style ) != "undefined" &&
                ( ( typeof ( current.style.display ) != "undefined" &&
                    current.style.display == "none" ) ||
                    ( typeof ( current.style.visibility ) != "undefined" &&
                        current.style.visibility == "hidden" ) ) ) ) {
            return false;
        }
        if( typeof ( current.parentNode ) != "undefined" &&
            current.parentNode != null &&
            current.parentNode != current &&
            (current.parentNode as HTMLElement).tagName.toLowerCase() != "body" ) {
            current = current.parentNode;
        }
        else {
            return true;
        }
    }
    return true;
}

但是tsc有两个编译器错误:

  • TS2339(TS)属性“ disabled”在类型“ HTMLElement”上不存在。
  • TS2740(TS)类型“节点和父节点”缺少类型“ HTMLElement”中的以下属性:accessKey,accessKeyLabel,autocapitalize,dir和178个以上。

我的快速解决方案是添加这些类型断言(如下所示),但是感觉就像我做错了什么。尤其是因为ctrl可以是HTMLSelectElementHTMLTextAreaElement,而它们不是HTMLInputElement但具有disabled: boolean属性:

function WebForm_IsInVisibleContainer( ctrl: HTMLElement ): boolean {
    var current = ctrl;
    while( ( typeof ( current ) != "undefined" ) && ( current != null ) ) {
        if( ( current as HTMLInputElement ).disabled ||                                // <-- here
            ( typeof ( current.style ) != "undefined" &&
                ( ( typeof ( current.style.display ) != "undefined" &&
                    current.style.display == "none" ) ||
                    ( typeof ( current.style.visibility ) != "undefined" &&
                        current.style.visibility == "hidden" ) ) ) ) {
            return false;
        }
        if( typeof ( current.parentNode ) != "undefined" &&
            current.parentNode != null &&
            current.parentNode != current &&
            (current.parentNode as HTMLElement).tagName.toLowerCase() != "body" ) {
            current = current.parentNode as HTMLElement;                                // <-- and here
        }
        else {
            return true;
        }
    }
    return true;
}

在JavaScript中,最好使用if( current.disabled )检查属性的存在-为什么TypeScript不支持此功能?

我知道另一种解决方法是像这样添加新界面:

interface DisableableHTMLElement extends HTMLElement {
    disabled: boolean;
}

function WebForm_IsInVisibleContainer( ctrl: DisableableHTMLElement ): boolean {
    // etc
}

...但这感觉更糟,而且也不简洁。

那么我该如何在TypeScript中执行以下操作:

function doSomething( foo: Element ): string {
    if( 'type' in foo ) {
        return foo.type as string;
    }
}

(当我确实使用上面的doSomething代码时,TypeScript说foo块内的if()类型实际上是never而不是Element

1 个答案:

答案 0 :(得分:1)

如果current(如果可能具有属性disabled)将是HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement之一,那么我认为最好的办法就是为此创建一个类型保护:

const isDisableableElement = (current: HTMLElement): current is HTMLInputElement | HTMLSelectElement  | HTMLTextAreaElement => {
    return 'disabled' in current;
};

这不是简明扼要的,而是像您说的那样:

  

我的快速解决方案是添加这些类型断言(如下所示),但是感觉就像我做错了什么。特别是因为ctrl可能是HTMLSelectElement的{​​{1}}或HTMLTextAreaElement,但确实具有HTMLInputElement属性

您必须在简洁和类型正确性之间进行选择,类型正确性可能是一个更好的选择,尤其是因为它不会添加太多的代码,简单明了且不会使未来的读者感到困惑。

然后,只需在检查disabled: boolean属性之前检查条件中的类型防护:

.disabled

如果您愿意,您似乎还可以减少代码中的其他检查。如果if ( (isDisableableElement(current) && current.disabled) || 参数输入正确,并且始终为ctrl,则它(及其父对象)将始终具有HTMLElementstyle的{​​{1}}属性子属性。 display也永远不会是visibilitycurrent,因为您已经在循环的底部进行了这些检查(而undefined不会是{{1 }}-它只会是一个元素或null)。代码永远不会超过循环的结尾-找到隐藏的父级并返回.parentNode,或者找到最终的父级并返回undefined

null