首页 > 解决方案 > HTMLElement 作为由 tagName 区分的联合

问题描述

我想让 TypeScript 知道,当我打开一个元素tagName并且案例是“输入”时,案例代码中的元素就是一个输入元素。我知道原则上可以使用可区分联合,但显然类型都不ElementHTMLElement可区分联合,因此以下代码失败:

function foo(e : HTMLElement) {
    switch(e.tagName) {
        case 'input':
            e.value = '123'; // Property 'value' does not exist on type 'HTMLElement'.
            break;
        case 'a':
            e.href = 'hi'; // Property 'href' does not exist on type 'HTMLElement'.
            break;
    }
}

因此,作为一种快速解决方法,我可以这样做:

interface TaggedLink extends HTMLLinkElement { 
    tagName: 'a'
}

interface TaggedInput extends HTMLInputElement {
    tagName: 'input'
}

type TaggedHTMLElement = TaggedLink | TaggedInput;

function foo(e : TaggedHTMLElement) {
    switch(e.tagName) {
        case 'input':
            e.value = '123'; // Good
            break;
        case 'a':
            e.href = 'hi'; // Good
            break;
    }
}

这行得通,但现在每个打电话的人foo都必须将参数转换为TaggedHTMLElement.

所以我想知道 TypeScript 是否已经带有一个元素作为可区分联合类型,我可以直接将其用作 foo 的参数类型,开箱即用,或者是否有更好的方法来做我正在尝试的事情至。我知道有一个名为的类型HTMLElementTagNameMap似乎很有用,但我认为我需要能够重新定义HTMLElement(类似于interface HTMLElement extends actual HTMLElement),以及某种方式来循环所有类型HTMLElementTagNameMap以生成类似于我定义的类型上面,然后将它们全部合并。但似乎这些事情都不可能发生。

标签: typescripttypesdiscriminated-union

解决方案


HTMLElement并且Element已经有tagName属性,它是只读的,只是一个string.

如果您想确切地知道哪个元素是,您必须对元素实例进行运行时检查。

function foo(e : HTMLLinkElement | HTMLInputElement) {
  if (e instanceof HTMLInputElement) {
    e.value = '123';
  }
  else if (e instanceof HTMLLinkElement) {
    e.href = 'hi';
  }
}


const el = document.querySelector<HTMLLinkElement>("a")

if (el) {
  foo(el)
}
``

推荐阅读