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

javascript - ใน Typescript ทำไมวัตถุจึงไม่สามารถระบุคุณสมบัติเพิ่มเติมเมื่อกำหนดค่า แต่สามารถทำได้เมื่อส่งไปยังฟังก์ชัน โดยใช้ Interface เดียวกัน?


ฉันได้เพิ่มโค้ดที่ทำให้ฉันเกิดข้อผิดพลาดไปยัง playground ของ TypeScript แล้ว คุณจะเห็นข้อผิดพลาดที่การกำหนดค่า object ด้านล่าง

interface Car {
    make: string,
    model: string,
    year: number
}

function repairCar(car: Car) {

}

let myCar = {
    make: 'Honda',
    model: 'Accord',
    year: 1992,
    color: 'red'
}

repairCar(myCar);

แต่เมื่อฉันเพิ่มโค้ดนี้:

let myCar2: Car = {
    make: 'Honda',
    model: 'Accord',
    year: 1992,
    color: 'red'
}

ฉันได้รับข้อผิดพลาดนี้:

ชนิด ' { make: string; model: string; year: number; color: string; } ' ไม่สามารถกำหนดค่าให้กับชนิด 'Car' ได้. วัตถุลิตรัลสามารถระบุได้เฉพาะคุณสมบัติที่รู้จักเท่านั้น และ 'color' ไม่มีอยู่ในชนิด 'Car'.

ฉันไม่เข้าใจว่าทำไมถึงอนุญาตให้มีคุณสมบัติเพิ่มเติมบนอ็อบเจ็กต์ที่ถูกส่งไปยังฟังก์ชัน repairCar แต่ไม่อนุญาตในตอนที่มันถูกกำหนดค่าให้กับตัวแปรที่มีการระบุชนิด มันเป็นอินเทอร์เฟซเดียวกัน

คำตอบ #1

ลองจินตนาการว่ามันจะทำงานโดยไม่มีข้อผิดพลาด:

  let myCar2: Car = {
    make: 'Honda',
    model: 'Accord',
    year: 1992,
    color: 'red'
 };

ดังนั้นคุณสมบัติสีจึงไร้ความหมายโดยสมบูรณ์ดังนี้:

 console.log(myCar2.color);

จะใช้ไม่ได้เพราะสีไม่ใช่ส่วนหนึ่งของประเภทรถยนต์

ทำไมจึง [แล้ว] อนุญาตให้มีสมบัติเกินในวัตถุที่ถูกส่งไปยังฟังก์ชัน repairCar?

เพราะคุณสามารถส่งต่อวัตถุที่มีคุณสมบัติเพิ่มเติมไปยังฟังก์ชันโดยไม่ส่งผลข้างเคียงใด ๆ และยังสามารถใช้คุณสมบัติสีได้ที่อื่น:

 repairCar(myCar);
 console.log(myCar.color); 

ตัวอย่างจากโลกจริง: มันไม่สมเหตุสมผลที่จะเรียกนกว่าเป็นสัตว์ เพราะถ้ามันเป็นเพียงสัตว์ มันจะบินไม่ได้ นั่นคือเหตุผลที่เราเรียกนกว่าเป็นนก ไม่ใช่สัตว์ อย่างไรก็ตามถ้าคุณบอกว่าสัตว์สามารถเคลื่อนไหวได้ นกก็สามารถเคลื่อนไหวได้เช่นกันเพราะมันเป็นสัตว์

ถ้าคุณต้องการแก้ไขข้อผิดพลาด ให้ทำสีเป็นตัวเลือก หรือใช้ซูเปอร์ไทป์:

 interface ColoredCar extends Car {
   color: string;
 }

 // or

 interface Car {
  make: string,
  model: string,
  year: number
  color?: string;
 }

คำตอบ #2

ออบเจ็กต์ใน Typescript ถูกอนุญาตให้มีเฉพาะฟิลด์ที่กำหนดในประเภทเท่านั้น แตกต่างจากภาษาอื่น ๆ เราไม่สามารถกำหนดตัวแปรเพื่อเก็บซับคลาสได้ ดังนั้นเราจึงได้รับข้อผิดพลาดเมื่อเขียน

interface Car = {
    make: string,
    model: string,
    year: number
}

let myCar: Car = {
    make: 'Honda',
    model: 'Accord',
    year: 1992,
    color: 'red'
}

อย่างไรก็ตาม Typescript อนุญาตให้เราดึงเมธอดด้วยออบเจ็กต์ที่มีฟิลด์ทั้งหมดที่เมธอดต้องการ การอนุญาตเช่นนี้จะไม่ทำให้เมธอดเสียเนื่องจากซับคลาสต้องมีการ implement ฟิลด์ทั้งหมดที่เมธอดต้องการ ดังนั้นเราจึงสามารถเขียนได้

interface ColoredCar extends Card {
    color: string
}

let myColoredCar: ColoredCar = {
    make: 'Honda',
    model: 'Accord',
    year: 1992,
    color: 'red'
}

function repairCar(car: Car) {
     // Implementation...
}

// Doesn't matter for the method repairCar if myColoredCar is a ColoredCar or a Car as 
// long as make, model and year are defined
repairCar(myColoredCar);

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