เหตุใด 'instanceof' ใน TypeScript จึงให้ข้อผิดพลาด "'Foo' หมายถึงประเภทเท่านั้น แต่ถูกใช้เป็นค่าที่นี่"?


93

ฉันเขียนโค้ดนี้

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เอง
Daniel Rosenwasser


และอาจทำซ้ำได้ของตัวแปร Check if เป็นประเภทอินเทอร์เฟซเฉพาะใน typescript union (ฉันไม่ต้องการตอกสิ่งนี้ด้วยมือเดียว)
Cerbrus

@Jenny O'Reilly ตอนนี้แน่นอนว่าซ้ำกับ Possible ซ้ำ!
marckassay

คำตอบ:


105

เกิดอะไรขึ้น

ปัญหาคือinstanceofสร้างจาก JavaScript และใน JavaScript instanceofคาดว่าจะมีค่าสำหรับตัวถูกดำเนินการด้านขวา โดยเฉพาะในx instanceof FooJavaScript จะทำการตรวจสอบรันไทม์เพื่อดูว่าFoo.prototypeมีอยู่ที่ใดในห่วงโซ่ต้นแบบของxมีอยู่ที่ใดก็ได้ในห่วงโซ่ต้นแบบของ

อย่างไรก็ตามใน TypeScript interfaceไม่มีการปล่อยออกมา นั่นหมายความว่าไม่ใช่FooหรือFoo.prototypeอยู่ที่รันไทม์ดังนั้นรหัสนี้แน่นอนจะล้มเหลว

TypeScript พยายามบอกคุณว่าสิ่งนี้ไม่สามารถใช้ได้Fooเป็นเพียงประเภทไม่ใช่มูลค่าเลย!

"ฉันจะทำอะไรแทน instanceofไหม”

คุณสามารถมองเข้าไปในยามชนิดและผู้ใช้กำหนดยามประเภท

"แต่ถ้าฉันเพิ่งเปลี่ยนจากinterface เป็นclass?"

คุณอาจถูกล่อลวงให้เปลี่ยนจาก an 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
คงต้องใช้เวลาหลายวัยเพื่อมองเห็นสิ่งนั้นด้วยตัวเอง!
Matthew Layton

โดยพื้นฐานแล้วฉันไม่ได้รับแนวคิดจากคำตอบที่ดีกว่าที่จะไปด้วย ชั้น? เพราะคุณให้รายละเอียด แต่สับสนในเวลาเดียวกันกับที่คุณพูดถึง "คุณอาจถูกล่อลวง". แล้วถ้าฉันต้องเปรียบเทียบคุณสมบัติทั้งหมดไม่ใช่แค่คุณสมบัติการว่ายน้ำเหมือนในเอกสารสำหรับ type guards?
HalfWebDev

8
ประเด็นหลักคือ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();
}

จะเกิดอะไรขึ้นถ้าฉันเรียนรู้เกี่ยวกับเป็ดและเพิ่มฟังก์ชันว่ายน้ำ () ในอินเทอร์เฟซ Bird ของฉัน สัตว์เลี้ยงทุกตัวจะไม่ถูกจัดให้เป็นปลาในยามประเภทหรือ? และถ้าฉันมีสามอินเทอร์เฟซที่มีสามฟังก์ชั่นแต่ละและสองทับซ้อนกับหนึ่งในอินเทอร์เฟซอื่น ๆ ?
Kayz

1
@Kayz หากคุณไม่มีคุณสมบัติ / ฟังก์ชันที่ระบุอินเทอร์เฟซโดยเฉพาะคุณจะไม่สามารถแยกความแตกต่างได้อย่างแท้จริง สัตว์เลี้ยงของคุณที่อาจจะจริงที่Duckคุณพิมพ์ยามมันจะกลายเป็นแต่ยังคงไม่มีข้อยกเว้นรันไทม์เมื่อคุณเรียกFish swim()แนะนำให้คุณสร้าง 1 ระดับของการติดต่อกัน (เช่นSwimmable) และย้ายคุณฟังก์ชั่นที่นั่นแล้วพิมพ์ยามยังคงมีลักษณะที่ดีกับswim() ((pet as Swimmable).swim
Lee Chee Kiam

เพื่อป้องกันการพิมพ์ดีดคุณสามารถใช้'swim' in petเงื่อนไข มันจะแคบลงไปเซตที่มีจะมีswimกำหนดไว้ (เช่น: Fish | Mammal)
Akxe

2

Daniel Rosenwasser อาจจะพูดถูกและสำรวย แต่ฉันรู้สึกอยากจะแก้ไขคำตอบของเขา เป็นไปได้อย่างเต็มที่ในการตรวจสอบอินสแตนซ์ของ 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.