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);
