Dlaczego „instanceof” w TypeScript wyświetla błąd „„ Foo ”odnosi się tylko do typu, ale jest tutaj używana jako wartość.”?

90

Napisałem ten kod

interface Foo {
    abcdef: number;
}

let x: Foo | string;

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

Ale TypeScript dał mi ten błąd:

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

Dlaczego to się dzieje? Pomyślałem, że instanceofmoże to sprawdzić, czy moja wartość ma określony typ, ale wydaje się, że TypeScript tego nie lubi.

Daniel Rosenwasser
źródło
Zobacz odpowiedź poniżej @ 4castle. W przeciwnym razie masz rację, dam radę Foo | string.
Daniel Rosenwasser
1
Możliwy duplikat sprawdzenia typu interfejsu za pomocą
Typescript
I możliwy duplikat Sprawdź, czy zmienna jest określonym typem interfejsu w unii maszynopisu (naprawdę nie chcę tego samodzielnie
wbijać
@Jenny O'Reilly, teraz to zdecydowanie duplikat możliwego duplikatu!
marckassay

Odpowiedzi:

100

Co się dzieje

Problem polega na tym, że instanceofjest to konstrukcja z JavaScript, aw JavaScript instanceofoczekuje wartości dla operandu po prawej stronie. W szczególności w x instanceof FooJavaScript przeprowadzi sprawdzenie w czasie wykonywania, aby sprawdzić, czy Foo.prototypeistnieje gdziekolwiek w łańcuchu prototypów x.

Jednak w TypeScript interfaces nie emitują. Oznacza to, że ani Foonie Foo.prototypeistnieje w czasie wykonywania, więc ten kod na pewno zawiedzie.

TypeScript próbuje ci powiedzieć, że to nigdy nie zadziała. Footo tylko typ, to wcale nie jest wartość!

„Co mogę zamiast tego zrobić instanceof?”

Możesz zajrzeć do zabezpieczeń typów i zabezpieczeń typów zdefiniowanych przez użytkownika .

- A co, jeśli właśnie przełączyłem się z an interface na a class?

Możesz pokusić się o przejście z a interfacena a class, ale powinieneś zdać sobie sprawę, że w systemie typów strukturalnych TypeScript (gdzie rzeczy są głównie oparte na kształtach ), możesz utworzyć dowolny obiekt, który ma taki sam kształt jak dana klasa:

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;

W tym przypadku masz xi ymają ten sam typ, ale jeśli spróbujesz użyć instanceofjednego z nich, uzyskasz odwrotny wynik na drugim. Więc instanceoftak naprawdę nie powie ci zbyt wiele o typie, jeśli korzystasz z typów strukturalnych w TypeScript.

Daniel Rosenwasser
źródło
2
Wieki zajęłoby mi znalezienie tego dla siebie!
Matthew Layton,
Więc w zasadzie nie doszedłem do pomysłu z odpowiedzi, która jest lepsza. Klasa? ponieważ wyszczególniłeś to. Ale zdezorientowany w tym samym czasie, gdy wspomniałeś „być może kusiłeś”. A co, jeśli będę musiał porównać wszystkie właściwości, a nie tylko właściwości pływania, jak w dokumentach dla typów strażników?
HalfWebDev
5
Najważniejsze jest to, że instanceofdziała z klasami, a nie interfejsami. Pomyślałem, że należy to podkreślić.
inorganik
5

Aby sprawdzić typ w czasie wykonywania za pomocą interfejsu, należy użyć ochrony typów , jeśli interfejsy, które chcesz sprawdzić, mają różne właściwości / funkcje.

Przykład

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
źródło
Co się stanie, jeśli dowiem się o kaczkach i dodam funkcję swim () do mojego interfejsu Birda? Czy każdy zwierzak nie zostałby sklasyfikowany jako ryba w typografii? A jeśli mam trzy interfejsy z trzema funkcjami, z których każda pokrywa się z jednym z pozostałych interfejsów?
Kayz
1
@Kayz, jeśli nie masz właściwości / funkcji, które jednoznacznie identyfikują interfejs, nie możesz ich tak naprawdę rozróżnić. Twój zwierzak, który może być w rzeczywistości Duckstrażnikiem, którym się stanie Fish, ale nadal nie ma wyjątku w czasie wykonywania, gdy wywołujesz swim(). Zaproponuj utworzenie 1 poziomu wspólnego interfejsu (np. Swimmable) I przeniesienie swim()tam funkcji, a następnie wpisz guard nadal dobrze wygląda z ((pet as Swimmable).swim.
Lee Chee Kiam
Aby zapobiec rzutowaniu, możesz użyć 'swim' in petwarunku. Będzie to sprowadzić do podzbioru że musi mieć swimzdefiniowany (np Fish | Mammal)
Akxe
2

Daniel Rosenwasser może mieć rację i elegancko, ale mam ochotę zmienić jego odpowiedź. W pełni można sprawdzić wystąpienie x, zobacz fragment kodu.

Ale równie łatwo jest przypisać x = y. Teraz x nie byłby instancją C, ponieważ y miałby tylko kształt 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
Elias Vesterlund
źródło