'HTMLElement'类型的值不存在属性'value'

Bja*_*sen 204 typescript

我正在玩打字稿,我正在尝试创建一个脚本,在输入框中输入文本时将更新p元素.

html看起来如下:

<html>
    <head>
    </head>
    <body>
        <p id="greet"></p>
        <form>
            <input id="name" type="text" name="name" value="" onkeyup="greet('name')" />
        </form>
    </body>
    <script src="greeter.js"></script>
</html>
Run Code Online (Sandbox Code Playgroud)

greeter.ts文件:

function greeter(person)
{
    return "Hello, " + person;
}

function greet(elementId)
{
    var inputValue = document.getElementById(elementId).value;

    if (inputValue.trim() == "")
        inputValue = "World";

    document.getElementById("greet").innerText = greeter(inputValue);
}
Run Code Online (Sandbox Code Playgroud)

当我编译时,tsc我得到以下"错误":

/home/bjarkef/sandbox/greeter.ts(8,53): The property 'value' does not exist on value of type 'HTMLElement'

但是编译器确实输出了一个javascript文件,它在chrome中运行得很好.

我怎么会得到这个错误?我该如何解决?

另外,在哪里可以'HTMLElement'根据打字稿查找哪些属性有效?

请注意我对javascript和打字稿很新,所以我可能会遗漏一些明显的东西.:)

Bja*_*sen 373

根据Tomasz Nurkiewicz的回答,"问题"是打字稿是类型安全的.:)所以document.getElementById()返回HTMLElement不包含value属性的类型.HTMLInputElement但是,子类型包含该value属性.

因此,一个解决方案是投的结果getElementById(),以HTMLInputElement这样的:

var inputValue = (<HTMLInputElement>document.getElementById(elementId)).value;
Run Code Online (Sandbox Code Playgroud)

<>是打字稿中的铸造操作符.请参阅问题TypeScript:cast HTMLElement.

上面的行中生成的javascript如下所示:

inputValue = (document.getElementById(elementId)).value;
Run Code Online (Sandbox Code Playgroud)

即不包含类型信息.

  • @Wildhammer 因为你的元素_不是_输入元素!OP 是。 (2认同)

Mic*_*ael 47

如果您正在使用反应,您可以使用as操作员.

let inputValue = (document.getElementById(elementId) as HTMLInputElement).value;
Run Code Online (Sandbox Code Playgroud)

  • 原因是因为在 tsx 文件中你不能使用 `&lt;&gt;` 操作符进行转换,因为它们是在 React 中使用的。所以你必须使用:https://basarat.gitbooks.io/typescript/docs/types/type-assertion.html#as-foo-vs-foo (3认同)
  • 我很确定“as”运算符是 TypeScript 注释,而不是 React。谢谢。 (3认同)
  • 由于某种原因,这里接受的答案对我提出了以下错误:JSX元素“ HTMLInputElement”没有相应的结束标记。这是对我有用的答案。 (2认同)

woo*_*san 38

尝试将要更新的元素强制转换为HTMLInputElement.如其他答案中所述,您需要向编译器提示这是特定类型的HTMLElement:

var inputElement = <HTMLInputElement>document.getElementById('greet');
inputElement.value = greeter(inputValue);
Run Code Online (Sandbox Code Playgroud)


Leo*_*Leo 16

更新的示例:

const inputElement: HTMLInputElement = document.getElementById('greet') as HTMLInputElement
const inputValue: string = InputElement.value
Run Code Online (Sandbox Code Playgroud)

说明文件:

TypeScript-基本类型-类型断言


Tom*_*icz 15

问题出在这里:

document.getElementById(elementId).value
Run Code Online (Sandbox Code Playgroud)

你知道HTMLElement返回getElementById()的实际上是一个HTMLInputElement继承它的实例,因为你传递了一个输​​入元素的ID.类似地,在静态类型的Java中,这将无法编译:

public Object foo() {
  return 42;
}

foo().signum();
Run Code Online (Sandbox Code Playgroud)

signum()是一种方法Integer,但编译器只知道静态类型foo(),即Object.并Object没有signum()方法.

但编译器无法知道,它只能基于静态类型,而不能基于代码的动态行为.就编译器所知,document.getElementById(elementId)表达式的类型没有value属性.只有输入元素才有价值.

用于参考检查HTMLElementHTMLInputElementMDN.我认为Typescript或多或少与这些一致.


inD*_*eam 14

对此的快速解决方法是使用[]选择属性.

function greet(elementId) {
    var inputValue = document.getElementById(elementId)["value"];
    if(inputValue.trim() == "") {
        inputValue = "World";
    }
    document.getElementById("greet").innerText = greeter(inputValue);
}
Run Code Online (Sandbox Code Playgroud)

我只是尝试了几种方法并找到了这个解决方案,
我不知道原始脚本背后的问题是什么.

作为参考,您可以参考Tomasz Nurkiewicz的帖子.


tou*_*ine 12

const a = document.getElementById("a")\nif (a instanceof HTMLInputElement) {\n    // a.value is valid here\n    console.log(a.value)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

更安全的方法

\n

上面的代码片段是 anwser 的要点;继续阅读以了解推理。

\n

大多数现有答案建议使用类型断言(类型转换)来完成这项工作,但有点像使用any\xe2\x80\x94 来禁用类型检查。有更好、更安全的方法。

\n

类型断言就像告诉 TypeScript 假装变量属于我们所说的类型。因此,TypeScript 将为该类型执行类型检查。如果我们犯了一个错误并告诉它错误的类型,我们就会产生一种错误的安全感,因为不会有编译警告,但在运行时会出现错误。让我们看一个例子:

\n
// <input id="a" value="1">\n// <div   id="b" value="2"></div>\n\nconst a = document.getElementById("a") as HTMLInputElement\nconst b = document.getElementById("b") as HTMLInputElement\n\nconsole.log(a.value) // 1\nconsole.log(b.value) // undefined\n
Run Code Online (Sandbox Code Playgroud)\n

我们已经告诉 TypeScriptab是 HTMLInputElement 类型,并且它会这样对待它们。但是,由于bHTMLDivElement 类型没有该value属性,b.value因此返回undefined

\n

使用类型保护装置缩小类型

\n

更好的方法是使用类型保护,它可以进行更好的控制。

\n

类型保护是一种在运行时确定变量类型的方法。类型断言只是说:“变量 x 属于 T 类型”,而类型防护则说:“如果变量 x 具有这些属性,则它属于 T 类型”。让我们快速看看类型保护的外观:

\n
const a = document.getElementById("a")\nif (a instanceof HTMLInputElement) {\n    // a == input element\n} else {\n    // a != input element\n}\n
Run Code Online (Sandbox Code Playgroud)\n

此类型防护仅检查是否a是 HTMLInputElement 的实例。如果是,TypeScript 将识别该类型并将aif 块内部视为该类型\xe2\x80\x94,它将允许访问输入元素的所有属性。这称为类型缩小。

\n

为什么应该使用类型保护而不是类型断言?出于同样的原因,您处理错误。虽然类型保护在编译时仍然不会输出警告,但它们让您可以控制。您(和 TypeScript)无法保证该value属性是否存在,但通过类型断言,您可以决定在不存在的情况下该怎么做。类型断言就像忽略错误,类型防护就像处理错误。

\n

如何使用类型保护

\n

我们将展示修复“该属性不存在”错误的三种方法。每个示例都使用相同的 HTML,但它单独包含在每个示例中,因此更易于阅读。

\n
    \n
  1. 类型断言
  2. \n
\n
// <input id="a" value="1">\n// <div   id="b" value="2">\n\nconst a = document.getElementById("a") as HTMLInputElement // correct type assertion\nconst b = document.getElementById("b") as HTMLInputElement // incorrect type assertion\nconst c = document.getElementById("c") as HTMLInputElement // element doesn\'t exist\n\nconsole.log(a.value) // 1\nconsole.log(b.value) // undefined\nconsole.log(c.value) // Uncaught TypeError: Cannot read property \'value\' of null\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. 直列式防护罩
  2. \n
\n
// <input id="a" value="1">\n// <div   id="b" value="2">\n\nconst a = document.getElementById("a")\nconst b = document.getElementById("b")\nconst c = document.getElementById("c")\n\nif (a instanceof HTMLInputElement) {\n    console.log(a.value) // 1\n}\nif (b instanceof HTMLInputElement) {\n    console.log(b.value)\n}\nif (c instanceof HTMLInputElement) {\n    console.log(c.value)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

b并且c没有记录任何内容,因为它们不是输入元素。请注意,我们没有像“类型断言”示例中那样得到任何意外行为。

\n

请注意,这是一个人为的示例,通常您会处理类型不是您期望的类型的情况。如果类型不匹配,我经常抛出错误,如下所示:

\n
if (!(b instanceof HTMLInputElement)) {\n    throw new Error("b is not an input element")\n}\n// b == input element (for the rest of this block)\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. 功能型防护罩
  2. \n
\n

这个例子有点高级,对于这个用例来说有点不必要。然而,它展示了功能类型防护和更“灵活”的类型防护。

\n

函数类型保护是确定给定值是否属于某种类型的函数。他们通过简单地返回一个布尔值来做到这一点。为了让 TypeScript 能够理解您正在创建类型保护,您必须使用类型谓词(请参阅下面示例中的注释)。

\n
// <input id="a" value="1">\n// <div   id="b" value="2">\n\nconst a = document.getElementById("a")\nconst b = document.getElementById("b")\nconst c = document.getElementById("c")\n\nif (hasValueProperty(a)) {\n    console.log(a.value) // 1\n}\nif (hasValueProperty(b)) {\n    console.log(b.value)\n}\nif (hasValueProperty(c)) {\n    console.log(c.value)\n}\n\nconst d = {\n    "value": "d",\n}\nif (hasValueProperty(d)) {\n    console.log(d.value) // d\n}\n\ntype WithValue = {\n    value: string\n}\n\n// hasValueProperty is a type guard function the determines whether x is of type\n// WithValue.\n//\n// "x is WithValue" is a type predicate that tells TypeScript that this is a\n// type guard.\nfunction hasValueProperty(x: unknown): x is WithValue {\n    return typeof x === "object" && x !== null && typeof (x as WithValue).value === "string"\n}\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,由于我们的类型保护仅检查“value”属性是否存在,因此它也可以用于任何其他对象(不仅仅是元素)。

\n


Kid*_*ode 11

对于使用Props或Refs等属性而没有"DocgetId"的人,您可以:

("" as HTMLInputElement).value;
Run Code Online (Sandbox Code Playgroud)

倒置引号是你的道具值,所以一个例子就是这样:

var val = (this.refs.newText as HTMLInputElement).value;
alert("Saving this:" + val);
Run Code Online (Sandbox Code Playgroud)


emm*_*kwu 7

对于那些可能仍在为此苦苦挣扎的人,另一种选择是使用(document.getElementById(elementId) as HTMLInputElement).value = ''. 来源

如果您仍然遇到问题,请尝试将其提取为如下函数:

function myInput() { (document.getElementById(elementId) as HTMLInputElement).value = '' }
Run Code Online (Sandbox Code Playgroud)


小智 6

如果您使用的是角度,则可以使用 -

const element = document.getElementById('elemId') as HTMLInputElement;
Run Code Online (Sandbox Code Playgroud)


Cer*_*nce 5

有一种方法可以在没有类型断言的情况下实现这一点,通过使用泛型来代替,通常使用起来更好更安全。

不幸的是,getElementById它不是通用的,而是querySelector

const inputValue = document.querySelector<HTMLInputElement>('#greet')!.value;
Run Code Online (Sandbox Code Playgroud)

类似地,您可以使用querySelectorAll来选择多个元素并使用泛型,以便 TS 可以理解所有选定的元素都属于特定类型:

const inputs = document.querySelectorAll<HTMLInputElement>('.my-input');
Run Code Online (Sandbox Code Playgroud)

这将产生一个NodeListOf<HTMLInputElement>.