อะไรคือความแตกต่างระหว่าง "ประกาศคลาส" และ "อินเทอร์เฟซ" ใน TypeScript


116

ใน TypeScript เมื่อสร้างไฟล์การประกาศซอร์ส. d.ts ซึ่งเป็นที่นิยมมากกว่าและเพราะเหตุใด

declare class Example {
    public Method(): void; 
}

หรือ

interface Example {
    Method(): void;
}

ความแตกต่างที่ฉันบอกได้คืออินเทอร์เฟซไม่สามารถมีเมธอดแบบคงที่ได้ดังนั้นคุณต้องใช้คลาสสำหรับสิ่งนั้น ทั้งสองไม่ได้ผลิตเอาต์พุต JS ใด ๆ ดังนั้นอาจไม่สำคัญ?



ฉันไม่รู้สึกว่าสิ่งเหล่านี้ช่วยอธิบายได้จริงๆว่าคุณจะใช้อีกอันหนึ่งเพราะทั้งคู่สามารถทำสิ่งเดียวกันได้โดยไม่มีเอาต์พุต JS
คริส

1
สั้น: ด้วยการประกาศคุณต้องตรวจสอบให้แน่ใจว่าการใช้งานคลาสนั้นมีอยู่ที่รันไทม์โดยที่คุณไม่จำเป็นต้องใช้อินเทอร์เฟซ
Hakim

คำตอบ:


163

interfaceมีไว้สำหรับเมื่อคุณต้องการอธิบายรูปร่างของวัตถุ ไม่มีการสร้างรหัสสำหรับอินเทอร์เฟซ - เป็นเพียงสิ่งประดิษฐ์ในระบบ type คุณจะไม่เห็นความแตกต่างในการสร้างโค้ดสำหรับคลาสโดยขึ้นอยู่กับว่ามีimplementsอนุประโยคหรือไม่

declare classมีไว้สำหรับเมื่อคุณต้องการอธิบายคลาสที่มีอยู่ (โดยปกติจะเป็นคลาส TypeScript แต่ไม่เสมอไป) ที่จะมีอยู่ภายนอก (ตัวอย่างเช่นคุณมีไฟล์. ts สองไฟล์ที่คอมไพล์เป็นไฟล์. js สองไฟล์และทั้งสองไฟล์จะรวมผ่านscriptแท็ก ในหน้าเว็บ) หากคุณสืบทอดมาจากการclassใช้extends(ไม่ว่าประเภทพื้นฐานจะเป็น a declare classหรือแบบธรรมดาclass) คอมไพเลอร์จะสร้างโค้ดทั้งหมดเพื่อเชื่อมโยงห่วงโซ่ต้นแบบและตัวสร้างการส่งต่อและสิ่งที่ไม่

หากคุณพยายามสืบทอดจากสิ่งdeclare classที่ควรเป็นอินเทอร์เฟซคุณจะมีข้อผิดพลาดรันไทม์เนื่องจากโค้ดที่สร้างขึ้นจะอ้างถึงอ็อบเจ็กต์ที่ไม่มีการแสดงรันไทม์

ในทางกลับกันหากคุณimplementเป็นเพียงอินเทอร์เฟซที่ควรเป็น a declare classคุณจะต้องนำสมาชิกทั้งหมดมาใช้ใหม่ด้วยตัวเองและจะไม่ใช้ประโยชน์จากการใช้โค้ดใด ๆ จากคลาสพื้นฐานและฟังก์ชันที่จะเป็น ที่ตรวจสอบโซ่ต้นแบบที่รันไทม์จะปฏิเสธวัตถุของคุณเนื่องจากไม่ได้เป็นอินสแตนซ์ของคลาสพื้นฐาน

ที่จะได้รับมันโง่ถ้าคุณมีพื้นหลัง c ++ คุณประมาณสามารถคิดinterfaceเป็นtypedefและdeclare classเป็นexternคำประกาศของผู้สร้างที่ไม่มีความหมายอย่างเคร่งครัดในหน่วยรวบรวมนี้

จากด้านการบริโภคอย่างแท้จริง (การเขียนโค้ดที่จำเป็นไม่ใช่การเพิ่มประเภทใหม่) ข้อแตกต่างเพียงอย่างเดียวระหว่างinterfaceและdeclare classคือคุณไม่สามารถnewใช้อินเทอร์เฟซได้ อย่างไรก็ตามหากคุณตั้งใจที่จะextend/ implementหนึ่งในประเภทเหล่านี้ในไฟล์classคุณอย่างจะต้องได้รับการแต่งตั้งอย่างถูกต้องได้ระหว่างและinterface declare classเพียงหนึ่งในนั้นจะทำงาน

กฎสองข้อที่จะตอบสนองคุณได้ดี:

  • เป็นชื่อของประเภทที่สอดคล้องกับฟังก์ชันคอนสตรัคเตอร์ (สิ่งที่เรียกได้new) ที่มีอยู่ในรันไทม์ (เช่นDateคือ แต่JQueryStaticไม่ใช่) ถ้าไม่คุณต้องการแน่นอนinterface
  • ฉันกำลังจัดการกับคลาสที่คอมไพล์แล้วจากไฟล์ TypeScript อื่นหรือมีอะไรที่คล้ายกันเพียงพอหรือไม่? ถ้าใช่ให้ใช้declare class

คุณสามารถใหม่อินเทอร์เฟซใน typescript ได้จริง ข้อ จำกัด เพียงอย่างเดียวคือการถ่ายทอดทางพันธุกรรม
Oleg Mihailik

3
คุณไม่สามารถเรียกใช้ตัวnewดำเนินการในประเภทอินเทอร์เฟซได้ อย่างไรก็ตามอินเทอร์เฟซสามารถสร้างลายเซ็นได้ซึ่งหมายความว่าคุณสามารถเรียกใช้ตัวnewดำเนินการตามค่าของประเภทอินเทอร์เฟซได้ สิ่งนี้แตกต่างจากวิธีการclassทำงานอย่างมากโดยที่ลายเซ็นการสร้างอยู่บนชื่อชนิดเองแทนที่จะเป็นนิพจน์ของประเภทนั้น
Ryan Cavanaugh

หากคุณไปตามเส้นทางของการเพิ่มตัวสร้างไปยังอินเทอร์เฟซควรเป็นสมาชิกของอินเทอร์เฟซเท่านั้นยกเว้น 'สถิตยศาสตร์' ของคลาส อย่ารวมอินเทอร์เฟซของฟังก์ชันคอนสตรัคเตอร์เข้ากับอินเทอร์เฟซของอ็อบเจ็กต์ที่สร้างขึ้น หากคุณทำเช่นนั้นระบบ type จะยอมให้มีความโง่เขลาเช่น: new (new x ()) โดยที่ x: Interface
Jeremy Bell

24

คุณสามารถใช้อินเทอร์เฟซ:

class MyClass implements Example {
    Method() {

    }
}

ในขณะที่declare classไวยากรณ์มีไว้เพื่อใช้ในการเพิ่มคำจำกัดความประเภทสำหรับโค้ดภายนอกที่ไม่ได้เขียนใน TypeScript ดังนั้นการนำไปใช้งานจึงเป็น "ที่อื่น"


ดังนั้นคุณกำลังแนะนำว่าควรใช้คลาสประกาศเพื่ออธิบายโค้ดที่ไม่ได้เขียนใน TypeScript? ฉันจะถือว่ากรณีนี้ แต่ในไฟล์ jquery.d.ts ที่เช็คอิน JQueryStatic เป็นอินเทอร์เฟซที่ดำเนินการโดย: ประกาศ var $: JQueryStatic ฉันจะมีแม้ว่าสิ่งนี้จะประกาศคลาส $ {public static ... }
Chris

เหตุผลเดียวที่ฉันคิดได้คือถ้าคุณไม่ต้องการให้คนอื่นขยายชั้นเรียน - การใช้อินเทอร์เฟซหมายความว่าคุณจะต้องจัดหาการใช้งานทั้งหมด
เฟนตัน

มีเหตุผล. บางทีนั่นอาจเป็นเหตุผล
คริส

ตกลงดังนั้นฉันพยายามชี้ไปที่การประกาศในไลบรารี JS อื่นดังนั้นฉันต้องการประกาศอย่างแน่นอน รหัสกำลังใช้สถิตกับฟังก์ชันไลบรารี (คลาส) บางส่วน - จนถึงตอนนี้ฉันยังไม่เห็นคุณสมบัติหรือวิธีการแบบคงที่ที่แสดงในไฟล์การประกาศ โอ้ฉันควรใช้เนมสเปซหรือโมดูลดี?
jenson-button-event

13

ในแง่ของคนธรรมดาdeclareใช้ใน.ts/ d.tsไฟล์เพื่อบอกคอมไพเลอร์ว่าเราควรคาดหวังว่าคีย์เวิร์ดที่เราdeclaringจะมีอยู่ในสภาพแวดล้อมนั้นแม้ว่าจะไม่ได้กำหนดไว้ในไฟล์ปัจจุบันก็ตาม สิ่งนี้จะช่วยให้เรามีความปลอดภัยในการพิมพ์เมื่อใช้อ็อบเจ็กต์ที่ประกาศเนื่องจากคอมไพเลอร์ typescript รู้แล้วว่าส่วนประกอบอื่น ๆ บางส่วนอาจให้ตัวแปรนั้น


6

ความแตกต่างระหว่างdeclareและinterfaceใน TS:

ประกาศ:

declare class Example {
    public Method(): void; 
}

ในโค้ดด้านบนdeclareช่วยให้คอมไพเลอร์ TS ทราบว่าExampleมีการประกาศคลาสไว้ที่ใดที่หนึ่ง นี่ไม่ได้หมายความว่าจะรวมคลาสไว้อย่างน่าอัศจรรย์ คุณในฐานะโปรแกรมเมอร์ต้องรับผิดชอบในการจัดให้มีชั้นเรียนพร้อมใช้งานเมื่อคุณประกาศ (ด้วยไฟล์declareคีย์เวิร์ด)

อินเตอร์เฟซ:

interface Example {
    Method(): void;
}

interfaceเป็นโครงสร้างเสมือนที่มีอยู่ใน typescript เท่านั้น คอมไพเลอร์ typescript ใช้เพื่อวัตถุประสงค์เดียวในการตรวจสอบประเภท เมื่อโค้ดถูกคอมไพล์ไปยัง JavaScript โครงสร้างทั้งหมดนี้จะถูกตัดออก คอมไพเลอร์ typescript ใช้อินเทอร์เฟซเพื่อตรวจสอบว่าออบเจ็กต์มีโครงสร้างที่ถูกต้องหรือไม่

ตัวอย่างเช่นเมื่อเรามีอินเทอร์เฟซต่อไปนี้:

interface test {
  foo: number,
  bar: string,
}

วัตถุที่เรากำหนดซึ่งมีประเภทอินเทอร์เฟซนี้จำเป็นต้องตรงกับอินเทอร์เฟซ:

// perfect match has all the properties with the right types, TS compiler will not complain.
  const obj1: test = {   
    foo: 5,
    bar: 'hey',
  }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.