Warum gibt mir 'instanceof' in TypeScript den Fehler "'Foo' bezieht sich nur auf einen Typ, wird hier aber als Wert verwendet."?

91

Ich habe diesen Code geschrieben

interface Foo {
    abcdef: number;
}

let x: Foo | string;

if (x instanceof Foo) {
    // ...
}

Aber TypeScript hat mir diesen Fehler gegeben:

'Foo' only refers to a type, but is being used as a value here.

Warum passiert das? Ich dachte, das instanceofkönnte überprüfen, ob mein Wert einen bestimmten Typ hat, aber TypeScript scheint dies nicht zu mögen.

Daniel Rosenwasser
quelle
Siehe die Antwort unten @ 4castle. Ansonsten hast du recht, ich werde es schaffen Foo | string.
Daniel Rosenwasser
1
Mögliches Duplikat der Schnittstellentypprüfung mit Typescript
Cerbrus
Und mögliches Duplikat von Überprüfen, ob die Variable ein bestimmter Schnittstellentyp in einer Typoskript-Union ist (ich möchte dies nicht wirklich im
Alleingang
@ Jenny O'Reilly, das ist definitiv ein Duplikat eines möglichen Duplikats!
Märzassay

Antworten:

100

Was ist los

Das Problem ist, dass instanceofes sich um ein Konstrukt aus JavaScript handelt und in JavaScript instanceofeinen Wert für den Operanden auf der rechten Seite erwartet . Insbesondere wird in x instanceof FooJavaScript eine Laufzeitprüfung durchgeführt, um festzustellen Foo.prototype, ob irgendwo in der Prototypenkette von vorhanden ist x.

In TypeScript haben interfaces jedoch keine Emission. Das bedeutet, dass zur Laufzeit weder Foonoch Foo.prototypevorhanden ist, sodass dieser Code definitiv fehlschlägt.

TypeScript versucht Ihnen mitzuteilen, dass dies niemals funktionieren könnte. Fooist nur ein Typ, es ist überhaupt kein Wert!

"Was kann ich tun anstatt instanceof?"

Sie können sich Typschutz und benutzerdefinierte Typenschutz ansehen .

"Aber was ist, wenn ich gerade von einem interface zu einem gewechselt bin class?"

Sie könnten versucht sein, von einem interfacezu einem zu wechseln class, aber Sie sollten sich darüber im Klaren sein, dass Sie im strukturellen Typensystem von TypeScript (bei dem die Dinge hauptsächlich formbasiert sind ) jedes Objekt erzeugen können, das dieselbe Form wie eine bestimmte Klasse hat:

class C {
    a: number = 10;
    b: boolean = true;
    c: string = "hello";
}

let x = new C()
let y = {
    a: 10, b: true, c: "hello",
}

// Works!
x = y;
y = x;

In diesem Fall haben Sie xund ydas haben den gleichen Typ, aber wenn Sie versuchen, eines instanceofvon beiden zu verwenden, erhalten Sie das andere Ergebnis auf dem anderen. Sie instanceofwerden also nicht wirklich viel über den Typ erzählen, wenn Sie strukturelle Typen in TypeScript nutzen.

Daniel Rosenwasser
quelle
2
Ich hätte ewig gebraucht, um das für mich selbst zu erkennen!
Matthew Layton
Im Grunde genommen kam mir die Idee nicht, was besser ist. Klasse? weil du es detailliert hast. Aber verwirrt zur gleichen Zeit, als Sie sagten "Sie könnten versucht sein". Was ist, wenn ich alle Eigenschaften vergleichen muss und nicht nur die Eigenschaften wie in den Dokumenten für Typwächter schwimmen muss?
HalfWebDev
5
Der Hauptpunkt hier ist, dass instanceofmit Klassen gearbeitet wird, nicht mit Schnittstellen. Das musste betont werden.
anorganik
5

Um zur Laufzeit eine Typprüfung mit einer Schnittstelle durchzuführen , werden Typschutzvorrichtungen verwendet , wenn die zu überprüfenden Schnittstellen unterschiedliche Eigenschaften / Funktionen haben.

Beispiel

let pet = getSmallPet();

if ((pet as Fish).swim) {
    (pet as Fish).swim();
} else if ((pet as Bird).fly) {
    (pet as Bird).fly();
}
Lee Chee Kiam
quelle
Was ist, wenn ich etwas über Enten lerne und meiner Bird-Oberfläche die Funktion Swim () hinzufüge? Würde nicht jedes Haustier als Fisch in einem Typwächter eingestuft? Und wenn ich drei Schnittstellen mit jeweils drei Funktionen habe und zwei sich mit einer der anderen Schnittstellen überschneiden?
Kayz
1
@ Kayz Wenn Sie keine Eigenschaften / Funktionen haben, die eine Schnittstelle eindeutig identifizieren, können Sie sie nicht wirklich unterscheiden. Ihr Haustier, das tatsächlich ein sein könnte Duck, geben Sie Guard ein, es wird Fish, aber immer noch keine Laufzeitausnahme, wenn Sie aufrufen swim(). Schlagen Sie vor, dass Sie eine Ebene der gemeinsamen Schnittstelle erstellen (z. B. Swimmable) und Ihre swim()Funktionen dorthin verschieben. Dann sieht Type Guard immer noch gut aus ((pet as Swimmable).swim.
Lee Chee Kiam
Um Typografie zu verhindern, können Sie 'swim' in petBedingung verwenden. Es wird es einzuengen auf eine Teilmenge nach unten , die haben muss swimdefiniert (zB: Fish | Mammal)
Akxe
2

Daniel Rosenwasser mag Recht und Recht haben, aber ich möchte seine Antwort ändern. Es ist vollständig möglich, die Instanz von x zu überprüfen, siehe das Code-Snippet.

Es ist aber genauso einfach, x = y zuzuweisen. Jetzt wäre x keine Instanz von C, da y nur die Form von C hatte.

class C {
a: number = 10;
b: boolean = true;
c: string = "hello";
}

let x = new C()
let y = {
    a: 10, b: true, c: "hello",
}

console.log('x is C? ' + (x instanceof C)) // return true
console.log('y is C? ' + (y instanceof C)) // return false
Elias Vesterlund
quelle