我正在玩打字稿,并试图创建一个脚本,该脚本将在输入框中输入文本时更新 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>
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);
}
当我用 tsc
编译时,我得到以下“错误”:
/home/bjarkef/sandbox/greeter.ts(8,53): The property 'value' does not exist on value of type 'HTMLElement'
但是编译器确实输出了一个 javascript 文件,该文件在 chrome 中运行良好。
我怎么会收到这个错误?我该如何解决?
另外,我在哪里可以根据打字稿查找 'HTMLElement'
上哪些属性有效?
请注意,我对 javascript 和 typescript 非常陌生,所以我可能会遗漏一些明显的东西。 :)
根据 Tomasz Nurkiewiczs 的回答,“问题”是打字稿是类型安全的。 :) 所以 document.getElementById()
返回不包含 value
属性的类型 HTMLElement
。然而,子类型 HTMLInputElement
确实包含 value
属性。
因此,一种解决方案是将 getElementById()
的结果转换为 HTMLInputElement
,如下所示:
var inputValue = (<HTMLInputElement>document.getElementById(elementId)).value;
<>
是 typescript 中的强制转换运算符。请参阅问题 TypeScript: casting HTMLElement。
如果您在 .tsx
文件中,上述转换语法将引发错误。您将希望改用此语法:
(document.getElementById(elementId) as HTMLInputElement).value
上面一行生成的 javascript 如下所示:
inputValue = (document.getElementById(elementId)).value;
即不包含类型信息。
如果您正在使用 react,则可以使用 as
运算符。
let inputValue = (document.getElementById(elementId) as HTMLInputElement).value;
<>
运算符进行强制转换,因为它们在 React 中使用。所以你必须使用:basarat.gitbooks.io/typescript/docs/types/…
as
运算符是 TypeScript 注释,而不是 React。谢谢。
(document.getElementById("addUserForm") as HTMLFormElement).reset();
我们可以assert
const inputElement: HTMLInputElement = document.getElementById('greet')
或使用 as
-syntax
const inputElement = document.getElementById('greet') as HTMLInputElement
给予
const inputValue = inputElement.value // now inferred to be string
尝试将要更新的元素转换为 HTMLInputElement。如其他答案所述,您需要向编译器提示这是一种特定类型的 HTMLElement:
var inputElement = <HTMLInputElement>document.getElementById('greet');
inputElement.value = greeter(inputValue);
对此的快速解决方法是使用 [ ] 选择属性。
function greet(elementId) {
var inputValue = document.getElementById(elementId)["value"];
if(inputValue.trim() == "") {
inputValue = "World";
}
document.getElementById("greet").innerText = greeter(inputValue);
}
我只是尝试了几种方法并找出了这个解决方案,我不知道您原始脚本背后的问题是什么。
作为参考,您可以参考 Tomasz Nurkiewicz 的帖子。
问题在这里:
document.getElementById(elementId).value
您知道从 getElementById()
返回的 HTMLElement
实际上是从它继承的 HTMLInputElement
的一个实例,因为您传递的是输入元素的 ID。同样,在静态类型的 Java 中,这不会编译:
public Object foo() {
return 42;
}
foo().signum();
signum()
是 Integer
的一个方法,但编译器只知道 foo()
的静态类型,即 Object
。并且 Object
没有 signum()
方法。
但是编译器不知道,它只能基于静态类型,而不是代码的动态行为。据编译器所知,document.getElementById(elementId)
表达式的类型没有 value
属性。只有输入元素才有价值。
对于 MDN 中的参考检查 HTMLElement
和 HTMLInputElement
。我猜 Typescript 或多或少与这些一致。
.value
,而是在 input
元素上访问它。还是我弄错了?
document.getElementById("greet")
并且您有 <p id="greet"></p>
...
document.getElementById("greet").innerText
,它应该是完全有效的,我只在包含以下行的输入元素上访问 value
属性:greet(elementId)
中的 var inputValue = document.getElementById(elementId).value;
并且在输入中的 html onkeyup="greet('name')"
中调用形式。所以我认为你误读了代码,如果我错了,请纠正我。 :)
getElementById
返回的值吗?我该如何在打字稿中做到这一点?还是有除 getElementById
之外的另一种方法返回 HTMLInputElement
?
同样对于使用 Props 或 Refs 等属性但没有您的“DocgetId”的任何人,您可以:
("" as HTMLInputElement).value;
倒引号是您的道具值,因此示例如下:
var val = (this.refs.newText as HTMLInputElement).value;
alert("Saving this:" + val);
对于那些可能仍在为此苦苦挣扎的人,另一种选择是使用 (document.getElementById(elementId) as HTMLInputElement).value = ''
。 source。
如果您仍然遇到问题,请尝试将其提取到如下函数:
function myInput() {
(document.getElementById(elementId) as HTMLInputElement).value = ''
}
有一种方法可以实现这种 没有 类型断言,通过使用 generics 来代替,通常使用起来更好更安全。
不幸的是,getElementById
不是通用的,但 querySelector
是:
const inputValue = document.querySelector<HTMLInputElement>('#greet')!.value;
同样,您可以使用 querySelectorAll
选择多个元素并使用泛型,以便 TS 可以了解所有选定的元素都是特定类型:
const inputs = document.querySelectorAll<HTMLInputElement>('.my-input');
这将产生一个 NodeListOf<HTMLInputElement>
。
checkJs
在 tsconfig
中为 true,并且目标:Es2020。使用 querySelector 并发送输入值时,到另一个网站的 fetch()
不起作用,而使用 getelementbyid
它可以工作。我该怎么办?
getElementById('foo')
并且有效,那么要转换为 querySelector
,您需要 querySelector('#foo')
和 #
。然后使用 .querySelector<HTMLElement>('#foo')
添加泛型
如果您使用的是角度,您可以使用 -
const element = document.getElementById('elemId') as HTMLInputElement;
const a = document.getElementById("a")
if (a instanceof HTMLInputElement) {
// a.value is valid here
console.log(a.value)
}
更安全的方式
上面的代码片段是anwser的要点;继续阅读推理。
大多数现有答案都推荐使用 Type assertions(类型转换),它可以完成这项工作,但有点像使用 any
- 它禁用类型检查。有一种更好、更安全的方法。
类型断言就像告诉 TypeScript 假装一个变量是我们所说的类型。因此,TypeScript 将为该类型执行类型检查。如果我们犯了错误并告诉它错误的类型,我们会得到一种错误的安全感,因为不会有编译警告,但在运行时会出现错误。让我们看一个例子:
// <input id="a" value="1">
// <div id="b" value="2"></div>
const a = document.getElementById("a") as HTMLInputElement
const b = document.getElementById("b") as HTMLInputElement
console.log(a.value) // 1
console.log(b.value) // undefined
我们已经告诉 TypeScript,a
和 b
是 HTMLInputElement 类型,它会这样对待它们。但是,由于 b
是没有 value
属性的 HTMLDivElement 类型,因此 b.value
返回 undefined
。
带有类型保护的类型缩小
更好的方法是使用允许更好控制的类型保护。
type guard 是一种在运行时确定变量类型的方法。虽然类型断言只是说:“变量 x 属于 T 类型”,但类型保护说:“如果变量 x 具有这些属性,则它属于 T 类型”。让我们快速看看类型保护的外观:
const a = document.getElementById("a")
if (a instanceof HTMLInputElement) {
// a == input element
} else {
// a != input element
}
这种类型保护只检查 a
是否是 HTMLInputElement 的实例。如果是,TypeScript 将识别它并将 if 块中的 a
视为该类型——它将允许访问输入元素的所有属性。这称为类型缩小。
为什么要使用类型保护而不是类型断言?出于同样的原因,您处理错误。虽然类型保护仍然不会在编译时输出警告,但它们让您可以控制。您(和 TypeScript)无法保证 value
属性是否存在,但通过类型断言,您可以决定在不存在的情况下该怎么做。 类型断言就像忽略错误,类型保护就像处理错误。
如何使用类型保护
我们将展示修复“属性不存在”错误的三种方法。每个示例都使用相同的 HTML,但它分别包含在每个示例中,因此更易于阅读。
类型断言
// <input id="a" value="1">
// <div id="b" value="2">
const a = document.getElementById("a") as HTMLInputElement // correct type assertion
const b = document.getElementById("b") as HTMLInputElement // incorrect type assertion
const c = document.getElementById("c") as HTMLInputElement // element doesn't exist
console.log(a.value) // 1
console.log(b.value) // undefined
console.log(c.value) // Uncaught TypeError: Cannot read property 'value' of null
内联型防护装置
// <input id="a" value="1">
// <div id="b" value="2">
const a = document.getElementById("a")
const b = document.getElementById("b")
const c = document.getElementById("c")
if (a instanceof HTMLInputElement) {
console.log(a.value) // 1
}
if (b instanceof HTMLInputElement) {
console.log(b.value)
}
if (c instanceof HTMLInputElement) {
console.log(c.value)
}
b
和 c
没有记录任何内容,因为它们不是输入元素。请注意,我们没有像“类型断言”示例中那样得到任何意外行为。
请注意,这是一个人为的示例,通常您会处理类型不是您预期的情况。如果类型不匹配,我经常抛出,如下所示:
if (!(b instanceof HTMLInputElement)) {
throw new Error("b is not an input element")
}
// b == input element (for the rest of this block)
函数类型守卫
对于这个用例来说,这个例子更高级一点,也有点不必要。但是,它展示了函数类型保护和更“灵活”的类型保护。
函数类型保护是确定给定值是否属于某种类型的函数。他们通过简单地返回一个布尔值来做到这一点。为了使 TypeScript 能够理解您正在制作类型保护,您必须使用类型谓词(参见下面示例中的注释)。
// <input id="a" value="1">
// <div id="b" value="2">
const a = document.getElementById("a")
const b = document.getElementById("b")
const c = document.getElementById("c")
if (hasValueProperty(a)) {
console.log(a.value) // 1
}
if (hasValueProperty(b)) {
console.log(b.value)
}
if (hasValueProperty(c)) {
console.log(c.value)
}
const d = {
"value": "d",
}
if (hasValueProperty(d)) {
console.log(d.value) // d
}
type WithValue = {
value: string
}
// hasValueProperty is a type guard function the determines whether x is of type
// WithValue.
//
// "x is WithValue" is a type predicate that tells TypeScript that this is a
// type guard.
function hasValueProperty(x: unknown): x is WithValue {
return typeof x === "object" && x !== null && typeof (x as WithValue).value === "string"
}
请注意,由于我们的类型保护只检查“值”属性的存在,它也可以用于任何其他对象(不仅仅是元素)。
这对我有用:
let inputValue = (swal.getPopup().querySelector('#inputValue ')as HTMLInputElement).value
swal
是什么意思?
我遇到了类似的问题 (TS warning in JS file: "Property X does not exist on type X": is it possible to write cleaner JavaScript?)
虽然该标记有助于删除 typescript 文件中的警告,但编译后我的 JavaScript 文件中仍然会收到警告。
那么如何编写干净且允许我操作 .value 的代码呢?
我花了很长时间,但我通过使用另一种方法找到了解决方案:
HTML 代码:
<form id="my-form"
action="index.html"
method="get"
onsubmit="return showValue();">
<input type="text" name="username">
<input type="text" name="full-name">
<input type="password" name="password">
<button type="button" onclick="showValue();">Show value</button>
</form>
Javascript代码:
function showValue() {
const myForm = document.forms.my-form;
console.log(myForm?.username.value);
return 1;
}
document.forms.x
有一个属性 "value"
,它会删除 typescript 文件和生成的 JavaScript 中的警告。
如果您有需要分配动态值的动态元素 ID,则可以使用:
//element_id = you dynamic id.
//dynamic_val = you dynamic value.
let _el = document.getElementById(element_id);
_el.value = dynamic_val.toString();
这对我有用。
这可能看起来很陈词滥调,但这对我有用:
yarn add -D @types/web
我想我一定有一个过时的类型定义。
问题:
错误“HTMLElement”类型上不存在属性“文本”
解决方案:在 typeScript 中,我们需要转换 document.getElementById()
,它在 < HTMLScriptElement >
中返回类型 HTMLElement
所以我们可以通过以下方式来解决 typescript.js 预期的错误
Code: var content: string = ( < HTMLScriptElement > document.getElementById(contentId)).text;
它对我有用..希望它也对你有用。