ค้นหาเว็บไซต์

ตัวกรองประเภท - ฉันจะใช้ instanceof ภายในตัวกรองประเภทที่กำหนดเองใน TypeScript ได้อย่างไร?


ฉันกำลังพยายามสร้างการป้องกันประเภทแบบกำหนดเองโดยใช้ instanceof แต่แปลกที่มันไม่ทำงานตามที่คาดไว้ในประโยค else

นี่คือตัวอย่างพร้อมลิงก์สนามเด็กเล่นที่เกี่ยวข้อง: ลิงก์สนามเด็กเล่น

class Person {}

class Animal {}

const isPerson = (obj: Person | Animal): obj is Person => obj instanceof Person;
const isAnimal = (obj: Person | Animal): obj is Animal => obj instanceof Animal;

const test: Person | Animal = new Person();

if(test instanceof Animal){
  test; // const test: Animal
}
else {
  test; // const test: Person
}

if(isAnimal(test)){
  test; // const test: Animal
}
else {
  test; // const test: never

}

ฉันคาดหวังให้ตัวแปร test เป็นชนิด Person ใน clause else แต่กลับเป็นชนิด never... ทำไมล่ะ?

ฉันรู้ว่าฉันสามารถใช้ instanceof โดยตรงได้เช่นกัน แต่ฉันอยากจะมีฟังก์ชันที่กระชับมากกว่านี้เหมือนกับที่สร้างขึ้น

อัปเดต: ด้วยการแก้ไขเล็ก ๆ นี้ ตัวตรวจสอบประเภททำงานถูกต้องแล้ว... ทำไมกัน?! ลิงก์ Playground

class Person {
  private xxx = "xxx"
}

class Animal {
  private xxx = "xxx"
}

const isPerson = (obj: Person | Animal): obj is Person => obj instanceof Person;
const isAnimal = (obj: Person | Animal): obj is Animal => obj instanceof Animal;

declare const test: Person | Animal;

if(test instanceof Animal){
  test; // const test: Animal
}
else {
  test; // const test: Person
}

if(isAnimal(test)){
  test; // const test: Animal
}
else {
  test; // const test: Person
}

คำตอบ #1

TypeScript ทำการกำหนดประเภทแบบโครงสร้าง และมันมองว่า Person และ Animal มีโครงสร้าง เหมือนกัน จริงๆ

ดังนั้นถ้า test is Animal เป็น false มันก็จะสรุปได้ว่า test is Person ก็เป็น false เช่นกัน ดังนั้นประเภท never จึงเหลืออยู่

โดยการมีคุณสมบัติที่ไม่ซ้ำกันใน Person และ AnimalPerson แก่คุณอย่างถูกต้องในบล็อก else

class Person {
  common = 0
  //person = 0
}

class Animal {
  common = 0
  animal = 0
}

const isPerson = (obj: Person | Animal): obj is Person => obj instanceof Person;
const isAnimal = (obj: Person | Animal): obj is Animal => obj instanceof Animal;

declare const test: Person | Animal;

if (test instanceof Animal) {
  test; // const test: Animal
}
else {
  test; // const test: Person
}

if (isAnimal(test)) {
  test; // const test: Animal
}
else {
  test; // const test: Person
}

ลิงก์สนามเด็กเล่น


หมายเหตุ: ระวัง const test: UnionType=oneOfTheTypes; TypeScript จะทำการลดขอบเขตการกำหนดค่า ดังนั้นมันยังสามารถคาดเดาได้ว่า test จริง ๆ แล้วเป็นหนึ่งในประเภทเท่านั้น


ในทางกลับกัน การจำกัดขอบเขตด้วย instanceof ทำงานแตกต่างออกไป:

ใน JavaScript x instanceof Foo ตรวจสอบว่าโซ่ต้นแบบของ x มี Foo.prototype หรือไม่.

TypeScript ชัดเจนว่าคัดลอกพฤติกรรมนี้ ดังนั้นมันจึงไม่ใช้การพิมพ์เชิงโครงสร้าง แต่ตรวจสอบการสืบทอดโปรโตไทป์


หลังจากแก้ไขคำถาม:

โดยการเพิ่มฟิลด์เฉพาะ (หรือฟิลด์ส่วนตัว) ให้กับ Animal เราจะทำให้ความเป็นไปได้ที่ Person จะสืบทอด Animal ถูกทำลายอย่างชัดเจน มันเป็นสถานการณ์ที่คล้ายกับตัวอย่างโค้ดของฉันด้านบน

ดังนั้นตอนนี้ TypeScript จะมองว่าพวกมันเป็นชนิดที่ไม่เกี่ยวข้องกันสองชนิดโดยสิ้นเชิง และการไหลของการควบคุมทำงานตามที่คุณคาดหวัง (เช่น test เป็น Person ในบล็อก else)


ในทางตรงกันข้าม หาก Person สืบทอด Animal เราจะ ระบุอย่างชัดเจน ความสัมพันธ์ระหว่าง 2 ประเภทนี้ และ TypeScript จะทราบว่า หาก test ไม่ใช่ Animal มันก็ไม่ใช่ Person ด้วยเช่นกัน (ดังนั้น test จะกลับไปเป็น never ในบล็อก else) รวมทั้งเมื่อใช้ instanceof! (เนื่องจากในกรณีนั้นเรากำลังสร้างการสืบทอดคลาส)

class Animal {
  private xxx = "xxx"
}

class Person extends Animal {
  private xxx2 = "xxx"
}

const isPerson = (obj: Person | Animal): obj is Person => obj instanceof Person;
const isAnimal = (obj: Person | Animal): obj is Animal => obj instanceof Animal;

declare const test: Person | Animal;

if (test instanceof Animal) {
  test; // const test: Animal | Person
}
else {
  test; // const test: never
}

if (isAnimal(test)) {
  test; // const test: Animal | Person
}
else {
  test; // const test: never
}

ลิงก์สนามเด็กเล่น

บทความที่เกี่ยวข้อง