Чому "instanceof" у TypeScript видає мені помилку "" Foo "стосується лише типу, але тут використовується як значення."?


91

Я написав цей код

interface Foo {
    abcdef: number;
}

let x: Foo | string;

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

Але TypeScript дав мені цю помилку:

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

Чому це відбувається? Я думав, що це instanceofможе перевірити, чи має моє значення певний тип, але TypeScript, схоже, не подобається це.


Дивіться відповідь нижче @ 4castle. Інакше ти маєш рацію, я встигну Foo | string.
Даніель Розенвассер


І можливий дублікат Перевірити, чи є змінна певним типом інтерфейсу в об’єднанні машинопису (я не дуже хочу це одноосібно забивати)
Cerbrus,

@Jenny O'Reilly, тепер це точно копія Можливого дубліката!
marckassay

Відповіді:


100

Що відбувається

Проблема в тому, що instanceofконструкція з JavaScript і в JavaScript instanceofочікує значення для правого операнда. Зокрема, в x instanceof FooJavaScript буде виконана перевірка часу виконання, щоб побачити, чи Foo.prototypeіснує де-небудь у ланцюжку прототипів x.

Однак у TypeScript interfaces не мають випромінювання. Це означає, що ні, Fooні Foo.prototypeіснує під час виконання, тому цей код точно не вдасться.

TypeScript намагається сказати вам, що це ніколи не могло спрацювати. Fooце просто тип, це зовсім не цінність!

"Що я можу зробити замість цього instanceof?"

Ви можете ознайомитись із захисниками типу та типовими захисами користувача .

"Але що, якби я просто перейшов з" interface на class"?"

У вас може виникнути спокуса перейти з a interfaceна a class, але ви повинні усвідомити, що в структурній системі типів TypeScript (де речі в основному засновані на фігурі ) ви можете створити будь-який об'єкт, який має таку ж форму, що і даний клас:

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;

У цьому випадку у вас є xі ті, yщо мають однаковий тип, але якщо ви спробуєте скористатися instanceofодним із них, ви отримаєте протилежний результат з іншого. Тож насправдіinstanceof не розповість вам багато про тип, якщо ви користуєтесь структурними типами в TypeScript.


2
Зайняв би мене вік, щоб помітити це для себе!
Метью Лейтон,

Тому в основному я не зрозумів ідеї з відповіді, з якою краще йти. Клас? тому що ви це деталізували. Але розгублений одночасно, коли ви згадували "вас, можливо, спокушали". То що, якщо мені доведеться порівнювати всі властивості, а не просто плавати, як у документах для охоронців типу?
HalfWebDev

5
Головне тут - instanceofробота з класами, а не з інтерфейсами. Думка, на якій потрібно було наголосити.
inorganik

5

Для перевірки типу під час виконання з інтерфейсом використовується захист типів , якщо інтерфейси, які ви хочете перевірити, мають різні властивості / функції.

Приклад

let pet = getSmallPet();

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

Що робити, якщо я дізнаюся про качок і додам функцію swim () до свого інтерфейсу Bird? Чи не кожна тварина буде класифікована як риба в охороні типу? А якщо у мене є три інтерфейси з трьома функціями кожна і дві перекриваються з одним з інших інтерфейсів?
Кайз,

1
@Kayz, якщо у вас немає властивостей / функцій, які однозначно ідентифікують інтерфейс, ви насправді не можете їх диференціювати. Ваш домашній улюбленець, яким насправді може бути Duck, ви вводите охоронця, яким він стає Fish, але все одно жодних винятків під час виконання виклику swim(). Запропонуйте створити 1 рівень загального інтерфейсу (наприклад Swimmable) і перенести swim()туди свої функції, після чого тип guard все ще добре виглядатиме ((pet as Swimmable).swim.
Lee Chee Kiam

Для запобігання набору тексту ви можете використовувати 'swim' in petумову. Це буде скоротити його до підмножини , який повинен бути swimвизначений (наприклад: Fish | Mammal)
Akxe

2

Даніель Розенвассер може бути правий і чудовий, але мені хочеться внести поправку в його відповідь. Цілком можливо перевірити екземпляр x, див. Фрагмент коду.

Але однаково легко призначити x = y. Тепер x не буде екземпляром C, оскільки y мав лише форму 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

-3

Перевірте, чи файл ur відповідає належному типу. Теги заборонені у файлах js, але дозволені у файлі jsx


1
Це не дає відповіді на запитання. Отримавши достатню репутацію, ви зможете коментувати будь-яку публікацію ; натомість надайте відповіді, які не вимагають роз’яснень від запитувача . - З огляду
Gad
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.