ความคงที่ - ตัวแก้ไข readonly ไม่สามารถสืบทอดเมื่อขยายคลาสได้หรือไม่? (TypeScript)
ฉันกำลังพยายามสร้างอินเทอร์เฟซแบบอ่านอย่างเดียวใน TS และนี่คือตัวอย่างที่ฉันได้ลองใช้กับตัวแก้ไข readonly ที่กระจายไปยังชนิดย่อย:
type JsonDatum =
| string
| boolean
| number
| readonly Json[]
| Object
| null
| undefined;
interface JsonInterface {
readonly [key: string]: Json;
}
type Json = JsonDatum | JsonInterface;
interface Player extends JsonInterface {
name: string;
countryCodes: number[];
elo: number;
}
const player: Player = {
name: "John Doe",
countryCodes: [1, 2, 3],
elo: 2000
}
player.name = "123" // No problem
player.countryCodes = [] // No problem
ฉันคิดว่า โดยการเพิ่มตัวแก้ไข readonly ให้กับ JsonInterface อินเทอร์เฟซที่สืบทอดมันก็จะมีคุณสมบัติ readonly ด้วย แต่ก็ไม่มีปัญหาอะไร
ในการทำให้มันใช้งานได้ ผมต้องใช้วิธีที่ไม่เป็น DRY เลยและเพิ่ม readonly ด้วยตนเองให้กับคุณสมบัติทั้งหมดของ subtype:
...
interface Player extends JsonInterface {
readonly name: string;
readonly countryCodes: number[];
readonly elo: number;
}
...
player.name = "123" // Cannot assign to 'name' because it is a read-only property.(2540)
player.countryCodes = [] // Cannot assign to 'name' because it is a read-only property.(2540)
นี่คือโค้ดบน playground ของ TypeScript
ฉันคิดว่าฉันจะเปลี่ยนวิธีการของฉันไปใช้ generic Readonly<T> เพราะดูเหมือนจะเป็นทางที่แนะนำมากกว่า แต่แม้กระทั่งตอนนั้นก็ยังดูเหมือนมีปัญหาอยู่
(คำนิยามของ Readonly<T> ดูเหมือนจะใกล้เคียงกับสิ่งที่ฉันกำลังทำอยู่: type Readonly<T>={ readonly [P in keyof T]: T[P]; }. และฉันก็ได้ลองปรับตัวอย่างข้างต้นโดยใช้ Readonly<T> แต่ไม่สำเร็จ.)
นี่เป็นพฤติกรรมที่คาดไว้หรือไม่? มีวิธีแก้ไขโดยไม่ใช้ Readonly<T> ไหม?
คำตอบ #1
นี่คือพฤติกรรมที่คาดไว้ ตามที่คุณเห็นที่นี่ Person สามารถถูกมองว่าเป็นการ "แทนที่" คำจำกัดความใน JsonInterface ผลลัพธ์จะเห็นได้ชัดเจนมากขึ้นในตัวอย่างที่แก้ไขนี้
ที่นี่สามารถเห็นได้ง่ายว่าแม้ว่า JsonInterface จะกำหนด readonly name แต่ interface Person ก็ได้แทนที่มัน
ข้อความตัวอย่างที่เป็นประโยชน์จากเอกสาร:
คีย์เวิร์ด
extendsบนอินเทอร์เฟซอนุญาตให้เราสามารถคัดลอกสมาชิกจากประเภทที่ตั้งชื่อไว้แล้วได้อย่างมีประสิทธิภาพ และเพิ่มสมาชิกใหม่ตามที่เราต้องการ สิ่งนี้สามารถเป็นประโยชน์ในการลดจำนวนโค้ดสำหรับการประกาศประเภทที่เราต้องเขียน และเพื่อสื่อถึงเจตนาว่าการประกาศคุณสมบัติเดียวกันหลายครั้งอาจเกี่ยวข้องกัน
จริง ๆ แล้วไม่มีวิธีแก้ปัญหาแบบง่าย ๆ สำหรับเรื่องนี้ แต่คุณควรจะใช้ Readonly<T> อยู่แล้ว (นั่นคือสิ่งที่มันถูกสร้างมาเพื่อ!). เพื่อที่จะ "ทำให้มันใช้งานได้" กับ Readonly คุณอาจลองทำดังนี้:
type Player = Readonly<{
name: string;
countryCodes: number[];
elo: number;
}>;
ซึ่งคุณสามารถเห็นได้ที่นี่ ส่งผลให้ได้ประเภทตามที่ต้องการ
