451 Stimmen

Die Eigenschaft 'value' existiert nicht bei einem Wert vom Typ 'HTMLElement'.

Ich spiele gerade mit Typescript herum und versuche, ein Skript zu erstellen, das ein p-Element aktualisiert, wenn Text in ein Eingabefeld eingegeben wird.

Die Html-Datei sieht wie folgt aus:

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

Und die greeter.ts Datei:

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

Wenn ich kompiliere mit tsc Ich erhalte den folgenden "Fehler":

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

Der Compiler gibt jedoch eine Javascript-Datei aus, die in Chrome einwandfrei funktioniert.

Wie kommt es, dass ich diese Fehlermeldung erhalte? Und wie kann ich ihn beheben?

Wo kann ich außerdem nachschlagen, welche Eigenschaften für eine 'HTMLElement' nach dem Typoskript?

Bitte beachten Sie, ich bin sehr neu zu javascript und typescript, so könnte ich etwas offensichtlich übersehen :)

21voto

Tomasz Nurkiewicz Punkte 322861

Das Problem liegt hier:

document.getElementById(elementId).value

Sie wissen, dass HTMLElement zurückgegeben von getElementById() ist eigentlich eine Instanz von HTMLInputElement geerbt, weil Sie eine ID des Eingabeelements übergeben. Auch in statisch typisiertem Java lässt sich dies nicht kompilieren:

public Object foo() {
  return 42;
}

foo().signum();

signum() ist eine Methode der Integer , aber der Compiler kennt nur den statischen Typ von foo() das ist Object . Und Object hat keine signum() Methode.

Aber der Compiler kann das nicht wissen, er kann nur auf statischen Typen basieren, nicht auf dem dynamischen Verhalten Ihres Codes. Und soweit der Compiler weiß, ist der Typ von document.getElementById(elementId) Ausdruck hat nicht value Eigentum. Nur Eingabeelemente haben einen Wert.

Für eine Referenzprüfung HTMLElement y HTMLInputElement in MDN. Ich denke, Typescript ist mehr oder weniger konsistent mit diesen.

16voto

emmaakachukwu Punkte 447

Für diejenigen, die damit immer noch Probleme haben, gibt es eine weitere Alternative: die Verwendung von (document.getElementById(elementId) as HTMLInputElement).value = '' . Quelle .

Sollten Sie dennoch Probleme damit haben, versuchen Sie es mit einer Funktion wie z.B. extrahieren:

function myInput() {
   (document.getElementById(elementId) as HTMLInputElement).value = ''
}

15voto

KidKode Punkte 179

Auch für alle, die Eigenschaften wie Props oder Refs ohne Ihre "DocgetId's" verwenden, können Sie das:

("" as HTMLInputElement).value;

Die invertierten Anführungszeichen stehen für den Wert Ihrer Requisiten, so dass ein Beispiel so aussehen würde:

var val = (this.refs.newText as HTMLInputElement).value;
alert("Saving this:" + val);

11voto

touchmarine Punkte 911
const a = document.getElementById("a")
if (a instanceof HTMLInputElement) {
    // a.value is valid here
    console.log(a.value)
}

Ein sicherer Weg

Der obige Codeschnipsel ist die Quintessenz der Antwort; lesen Sie weiter, um die Gründe zu erfahren.

Die meisten vorhandenen Antworten empfehlen Typ-Assertionen (type casts), die die Aufgabe erfüllen, aber ein bisschen so sind wie die Verwendung von any -was die Typüberprüfung deaktiviert. Es gibt einen besseren, sichereren Weg.

Mit Type Assertions wird TypeScript angewiesen, so zu tun, als ob eine Variable den von uns angegebenen Typ hätte. Als solches führt TypeScript eine Typüberprüfung durch für diesen Typ . Wenn wir einen Fehler gemacht und den falschen Typ angegeben haben, werden wir ein falsches Gefühl der Sicherheit bekommen, da es keine Kompilierungswarnungen geben wird, aber es werden Fehler zur Laufzeit auftreten. Schauen wir uns ein Beispiel an:

// <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

Wir haben TypeScript gesagt, dass a y b sind vom Typ HTMLInputElement und werden als solche behandelt. Da jedoch b ist vom Typ HTMLDivElement, der nicht die Eigenschaft value Eigentum, b.value gibt zurück. undefined .

Typenverengung mit Typenschützern

Ein besserer Weg ist die Verwendung von Type Guards, die eine bessere Kontrolle ermöglichen.

A Typschutz ist eine Möglichkeit, den Typ einer Variablen zur Laufzeit zu bestimmen. Während Typ-Assertions einfach sagen: "Variable x ist vom Typ T", sagen Typgarantien: "Die Variable x ist vom Typ T, wenn sie diese Eigenschaften hat". Schauen wir uns kurz an, wie ein Type Guard aussieht:

const a = document.getElementById("a")
if (a instanceof HTMLInputElement) {
    // a == input element
} else {
    // a != input element
}

Dieser Typ Guard prüft einfach, ob a ist eine Instanz von HTMLInputElement. Wenn es das ist, erkennt TypeScript das und behandelt a innerhalb des if-Blocks als von diesem Typ - es wird den Zugriff auf alle Eigenschaften des Eingabeelements ermöglichen. Dies wird als "type narrowing" bezeichnet.

Warum sollten Sie Type Guards statt Type Assertions verwenden? Aus demselben Grund, aus dem Sie Fehler behandeln. Typ-Guards geben zwar immer noch keine Warnungen zur Kompilierzeit aus, aber sie geben Ihnen die Kontrolle. Sie (und TypeScript) können nicht garantieren, ob die value Eigenschaft vorhanden ist, aber mit Typ-Assertions können Sie entscheiden, was zu tun ist, wenn dies nicht der Fall ist. Type Assertions sind wie das Ignorieren von Fehlern und Type Guards sind wie deren Behandlung.

Verwendung von Typensicherungen

Wir werden drei Möglichkeiten zeigen, wie der Fehler "Die Eigenschaft existiert nicht" behoben werden kann. In jedem Beispiel wird derselbe HTML-Code verwendet, aber er ist in jedem Beispiel separat enthalten, damit es leichter zu lesen ist.

  1. Typ-Assertionen

    // <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

  2. Inline-Typ Schutzvorrichtungen

    // <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 y c haben nichts protokolliert, da es sich nicht um Eingabeelemente handelt. Beachten Sie, dass kein unerwartetes Verhalten wie in den Beispielen für "type assertions" auftrat.

Beachten Sie, dass dies ein erfundenes Beispiel ist. Normalerweise würden Sie die Fälle behandeln, in denen die Typen nicht die erwarteten sind. Ich werfe oft, wenn ein Typ nicht übereinstimmt, wie so:

if (!(b instanceof HTMLInputElement)) {
    throw new Error("b is not an input element")
}
// b == input element (for the rest of this block)
  1. Funktionstyp Schutzvorrichtungen

Dieses Beispiel ist etwas fortgeschrittener und für diesen Anwendungsfall unnötig. Es zeigt jedoch Funktionstypgarantien und eine "flexiblere" Typgarantie.

Funktionstyp-Guards sind Funktionen, die feststellen, ob der angegebene Wert von einem bestimmten Typ ist. Sie tun dies, indem sie einfach ein bool zurückgeben. Damit TypeScript erkennt, dass es sich um einen Type Guard handelt, müssen Sie ein Type-Prädikat verwenden (siehe Kommentar im Beispiel unten).

// <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"
}

Da unsere Typüberwachung nur das Vorhandensein der Eigenschaft "value" prüft, kann sie auch für jedes andere Objekt (nicht nur für Elemente) verwendet werden.

10voto

CertainPerformance Punkte 306402

Es gibt eine Möglichkeit, dies zu erreichen sin Typbehauptung, durch Verwendung von Generika die im Allgemeinen etwas schöner und sicherer zu verwenden sind.

Leider, getElementById ist nicht generisch, sondern querySelector es:

const inputValue = document.querySelector<HTMLInputElement>('#greet')!.value;

In ähnlicher Weise können Sie querySelectorAll um mehrere Elemente auszuwählen und Generika zu verwenden, so dass TS verstehen kann, dass alle ausgewählten Elemente von einem bestimmten Typ sind:

const inputs = document.querySelectorAll<HTMLInputElement>('.my-input');

Dadurch wird eine NodeListOf<HTMLInputElement> .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X