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

ความคงที่ - ตัวแก้ไข 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;
}>;

ซึ่งคุณสามารถเห็นได้ที่นี่ ส่งผลให้ได้ประเภทตามที่ต้องการ