Perché 'instanceof' in TypeScript mi ​​dà l'errore "'Foo' si riferisce solo a un tipo, ma qui viene utilizzato come valore."?


93

Ho scritto questo codice

interface Foo {
    abcdef: number;
}

let x: Foo | string;

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

Ma TypeScript mi ​​ha dato questo errore:

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

Perché sta succedendo? Ho pensato che instanceofpotesse verificare se il mio valore ha un determinato tipo, ma a TypeScript non sembra piacere.


Vedi la risposta qui sotto @ 4castle. Altrimenti hai ragione, ce la farò Foo | string.
Daniel Rosenwasser

1
Possibile duplicato del controllo del tipo
Cerbrus


@ Jenny O'Reilly, ora è sicuramente un duplicato di un possibile duplicato!
marckassay

Risposte:


105

Cosa sta succedendo

Il problema è che instanceofè un costrutto di JavaScript e, in JavaScript, si instanceofaspetta un valore per l'operando di destra. In particolare, in x instanceof FooJavaScript eseguirà un controllo in fase di esecuzione per vedere se Foo.prototypeesiste ovunque nella catena di prototipi di x.

Tuttavia, in TypeScript, interfaces non hanno emit. Ciò significa che né FooFoo.prototypeesistono in fase di esecuzione, quindi questo codice fallirà sicuramente.

TypeScript sta cercando di dirti che questo non potrebbe mai funzionare. Fooè solo un tipo, non è affatto un valore!

"Cosa posso fare invece di instanceof?"

Puoi esaminare protezioni dei tipi e quelle definite dall'utente .

"Ma cosa succederebbe se passassi da an interface a aclass ?"

Potresti essere tentato di passare da a interfacea a class, ma dovresti renderti conto che nel sistema di tipi strutturali di TypeScript (dove le cose sono principalmente basate sulla forma ), puoi produrre qualsiasi oggetto che abbia la stessa forma di una data classe:

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 questo caso, hai xe yhai lo stesso tipo, ma se provi a usarli instanceofsu uno dei due, otterrai il risultato opposto sull'altro. Quindi instanceofnon sarà davvero dirvi molto circa il tipo se si sta approfittando di tipologie strutturali a macchina.


2
Mi ci sarebbero voluti anni per individuarlo da solo!
Matthew Layton

Quindi fondamentalmente non ho avuto l'idea dalla risposta con cui è meglio andare. Classe? perché l'hai dettagliato. Ma confuso nello stesso momento in cui hai detto "forse sei tentato". Quindi cosa succede se devo confrontare tutte le proprietà e non solo le proprietà di nuoto come nei documenti per le guardie di tipo?
HalfWebDev

8
Il punto principale qui è che instanceoffunziona con le classi, non con le interfacce. Pensato che doveva essere enfatizzato.
inorganik

5

Per eseguire il controllo del tipo in fase di esecuzione con un'interfaccia si utilizzano le protezioni dei tipi , se le interfacce che si desidera controllare hanno diverse proprietà / funzioni.

Esempio

let pet = getSmallPet();

if ((pet as Fish).swim) {
    (pet as Fish).swim();
} else if ((pet as Bird).fly) {
    (pet as Bird).fly();
}

E se venissi a conoscenza delle anatre e aggiungessi la funzione swim () alla mia interfaccia Bird? Ogni animale domestico non sarebbe classificato come pesce in una guardia di tipo? E se ho tre interfacce con tre funzioni ciascuna e due si sovrappongono con una delle altre interfacce?
Kayz

1
@ Kayz se non hai proprietà / funzioni che identificano in modo univoco un'interfaccia, non puoi davvero differenziarle. Il tuo animale domestico che potrebbe essere effettivamente un Duck, digiti guard diventa Fish, ma ancora nessuna eccezione di runtime quando lo inviti swim(). Suggerisco di creare 1 livello di interfaccia comune (ad es. Swimmable) E spostare le swim()funzioni lì, quindi digitare guard sembra ancora buono con ((pet as Swimmable).swim.
Lee Chee Kiam

Per evitare il typecasting puoi usare 'swim' in petcondition. Sarà restringere il campo a un sottoinsieme che deve essere swimdefinita (es: Fish | Mammal)
Akxe

2

Daniel Rosenwasser potrebbe avere ragione e dandy, ma mi sento di fare un emendamento alla sua risposta. È completamente possibile controllare l'istanza di x, vedere lo snippet di codice.

Ma è altrettanto facile assegnare x = y. Ora x non sarebbe un'istanza di C poiché y aveva solo la forma di C.

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.