Apa perbedaan antara "mendeklarasikan kelas" dan "antarmuka" di TypeScript

116

Di TypeScript, saat membuat file deklarasi sumber .d.ts, mana yang lebih disukai dan mengapa?

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

atau

interface Example {
    Method(): void;
}

Perbedaan yang dapat saya ceritakan adalah bahwa antarmuka tidak dapat memiliki metode statis, jadi Anda harus menggunakan kelas untuk itu. Keduanya tidak menghasilkan keluaran JS, jadi mungkin itu tidak masalah?

Chris
sumber
Saya tidak merasa bahwa salah satu dari ini benar-benar membantu menjelaskan Anda akan menggunakan satu sama lain karena keduanya dapat mencapai hal yang sama tanpa keluaran JS.
Chris
1
Singkatnya: Dengan mendeklarasikan Anda harus memastikan bahwa implementasi kelas ada saat runtime di mana dengan antarmuka Anda tidak perlu melakukannya.
Hakim

Jawaban:

163

interfaceadalah saat Anda hanya ingin mendeskripsikan bentuk suatu objek. Tidak ada pembuatan kode, selamanya, untuk antarmuka - mereka hanya artefak dalam sistem tipe. Anda tidak akan melihat perbedaan dalam pembuatan kode untuk sebuah kelas bergantung pada apakah kelas tersebut memiliki implementsklausa atau tidak .

declare classadalah ketika Anda ingin mendeskripsikan kelas yang ada (biasanya kelas TypeScript, tetapi tidak selalu) yang akan hadir secara eksternal (misalnya, Anda memiliki dua file .ts yang dikompilasi menjadi dua file .js dan keduanya disertakan melalui scripttag di halaman web). Jika Anda mewarisi dari classmenggunakan extends(terlepas dari apakah tipe dasar adalah declare classatau biasa class) compiler akan menghasilkan semua kode untuk menghubungkan rantai prototipe dan forwarding konstruktor dan apa yang tidak.

Jika Anda mencoba untuk mewarisi dari declare classyang seharusnya menjadi antarmuka, Anda akan mengalami kesalahan waktu proses karena kode yang dihasilkan akan merujuk ke objek tanpa manifestasi waktu proses.

Sebaliknya, jika Anda hanya implementsebuah antarmuka yang seharusnya menjadi declare class, Anda harus menerapkan ulang semua anggota sendiri dan tidak akan mengambil keuntungan dari penggunaan ulang kode apa pun dari kelas dasar calon, dan fungsi yang memeriksa rantai prototipe saat runtime akan menolak objek Anda karena tidak benar-benar menjadi turunan dari kelas dasar.

Untuk menjadi benar-benar kutu buku, jika Anda memiliki latar belakang C ++, Anda secara kasar dapat menganggap interfacesebagai typedefdan declare classsebagai externdeklarasi konstruktor yang benar-benar tidak memiliki definisi dalam unit kompilasi ini.

Dari sisi konsumsi murni (menulis kode imperatif, bukan menambahkan tipe baru), satu-satunya perbedaan antara interfacedan declare classadalah Anda tidak dapat newmembuat antarmuka. Namun, jika Anda bermaksud untuk extend/ implementsalah satu dari jenis ini di baru class, Anda benar-benar harus memilih dengan benar antara interfacedan declare class. Hanya satu yang akan berhasil.

Dua aturan yang akan membantu Anda dengan baik:

  • Apakah nama tipe sejajar dengan fungsi konstruktor (sesuatu yang dapat dipanggil dengan new) yang benar-benar ada saat runtime (misalnya Date, tetapi JQueryStatictidak)? Jika tidak , Anda pasti ingininterface
  • Apakah saya berurusan dengan kelas yang dikompilasi dari file TypeScript lain, atau sesuatu yang cukup mirip? Jika ya , gunakandeclare class
Ryan Cavanaugh
sumber
Anda bisa membuat antarmuka baru dalam skrip ketikan. Satu-satunya batasan adalah warisan.
Oleg Mihailik
3
Anda tidak dapat memanggil newoperator pada tipe antarmuka. Namun, antarmuka dapat memiliki tanda tangan konstruksi, yang berarti Anda dapat memanggil newoperator pada nilai tipe antarmuka. Ini sangat berbeda dari cara classkerjanya, di mana tanda tangan konstruksi berada pada nama tipe itu sendiri dan bukan pada ekspresi tipe itu.
Ryan Cavanaugh
Jika Anda pergi ke rute menambahkan konstruktor ke antarmuka, itu harus menjadi HANYA anggota antarmuka, kecuali untuk 'statika' kelas. Jangan gabungkan antarmuka fungsi konstruktor dengan antarmuka objek yang dibangun. Jika Anda melakukannya, sistem tipe memungkinkan kekonyolan seperti: baru (baru x ()), di mana x: Antarmuka.
Jeremy Bell
24

Anda dapat mengimplementasikan antarmuka:

class MyClass implements Example {
    Method() {

    }
}

Sedangkan declare classsintaks sebenarnya dimaksudkan untuk digunakan untuk menambahkan definisi tipe untuk kode eksternal yang tidak tertulis dalam TypeScript - jadi implementasinya adalah "di tempat lain".

Fenton
sumber
Jadi Anda menyarankan agar kelas deklarasi harus digunakan untuk mendeskripsikan kode yang tidak ditulis dalam TypeScript? Saya akan berasumsi demikian, tetapi dalam file jquery.d.ts yang diperiksa, JQueryStatic adalah antarmuka yang diimplementasikan oleh: mendeklarasikan var $: JQueryStatic Saya akan berpikir ini untuk menyatakan kelas $ {public static ...}
Chris
Satu-satunya alasan yang dapat saya pikirkan untuk ini adalah jika Anda tidak ingin orang memperluas kelas - menggunakan antarmuka berarti Anda harus menyediakan seluruh implementasi.
Fenton
Masuk akal. Mungkin itu alasannya.
Chris
Oke, jadi saya mencoba menunjuk ke deklarasi di pustaka JS lain jadi saya pasti perlu mendeklarasikan. Kode menggunakan statika pada beberapa fungsi perpustakaan (kelas) - sejauh ini saya belum melihat properti atau metode statis yang diekspresikan dalam file deklarasi. Oh, dan haruskah saya menggunakan namespace atau modul?
jenson-button-event
13

Dalam istilah awam, declaredigunakan di .ts/ d.tsfiles untuk memberi tahu kompiler bahwa kita harus mengharapkan kata kunci kita declaringada di lingkungan itu, bahkan jika itu tidak ditentukan dalam file saat ini. Ini kemudian akan memungkinkan kita untuk memiliki keamanan tipe saat menggunakan objek yang dideklarasikan, karena compiler Typecript sekarang tahu bahwa beberapa komponen lain mungkin menyediakan variabel itu.

nikk wong
sumber
6

Perbedaan antara declaredan interfacedi TS:

menyatakan:

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

Dalam kode di atas declarememungkinkan kompiler TS mengetahui bahwa di suatu tempat kelas Exampledideklarasikan. Ini tidak berarti bahwa kelas tersebut disertakan secara ajaib. Anda sebagai programmer bertanggung jawab untuk menyediakan kelas saat Anda mendeklarasikannya (dengan declarekata kunci).

antarmuka:

interface Example {
    Method(): void;
}

An interfaceadalah konstruksi virtual yang hanya ada dalam skrip ketikan. Kompilator naskah ketikan menggunakannya hanya untuk tujuan pemeriksaan jenis. Ketika kode dikompilasi ke javascript seluruh konstruksi ini akan dilucuti. Kompilator skrip menggunakan antarmuka untuk memeriksa apakah objek memiliki struktur yang benar.

Misalnya ketika kita memiliki antarmuka berikut:

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

Objek yang kita definisikan yang memiliki tipe antarmuka ini harus sama persis dengan antarmuka:

// perfect match has all the properties with the right types, TS compiler will not complain.
  const obj1: test = {   
    foo: 5,
    bar: 'hey',
  }
Willem van der Veen
sumber