Menu

Inheritance di TypeScript: extends, super, dan override

Pewarisan class di TypeScript: extends dan super, meng-override method dengan tipe yang kompatibel, kata kunci override dan noImplicitOverride, member protected, mendeklarasikan ulang field dengan declare, dan kapan implements adalah alat yang lebih tepat.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Class TypeScript mewarisi class lain dengan extends. Subclass mendapatkan setiap field dan method milik parent, memanggil constructor parent dengan super(...), dan bisa meng-override method, sementara TypeScript memeriksa bahwa setiap override tetap kompatibel dengan tipe milik parent.

Ini adalah pewarisan class JavaScript (lihat inheritance di JavaScript untuk prototype chain di baliknya). TypeScript menambahkan pemeriksaan tipe: Dog bisa di-assign di mana pun Animal diharapkan, dan compiler memverifikasi override serta pemanggilan super.

Meng-override Method

Method subclass dengan nama yang sama menggantikan method milik parent. Tipenya harus bisa di-assign ke tipe method parent: parameter yang sama atau kompatibel, dan tipe kembalian yang sama atau lebih sempit. Kalau tidak, kode apa pun yang memegang Animal bisa memanggil method itu dengan argumen yang tidak bisa ditangani subclass.

Mengubah parameternya menjadi value: string akan memunculkan error TS2416: Property 'format' in type 'CurrencyFormatter' is not assignable to the same property in base type 'Formatter'. Perulangan di atas menunjukkan alasannya: ia memanggil format dengan angka pada setiap elemen, tanpa tahu subclass mana yang sedang dipegangnya.

Ada satu celah. Parameter method dibandingkan secara bivarian, jadi override yang menerima tipe lebih sempit, misalnya format(value: 1 | 2), tetap lolos kompilasi walaupun perulangan tadi masih bisa mengirim 1234567. Tipe kembalian diperiksa dengan ketat: mengembalikan number alih-alih string adalah TS2416.

Kata Kunci override dan noImplicitOverride

override menyatakan "member ini menggantikan member di base class", dan compiler memegangmu pada janji itu. Kalau base class tidak punya member tersebut (salah ketik, atau method yang namanya diubah seseorang di parent), hasilnya error:

index.ts(9,14): error TS4117: This member cannot have an 'override' modifier because it is not declared in the base class 'Animal'. Did you mean 'speak'?

Tanpa override, salah ketik itu lolos kompilasi dan speak() diam-diam mengembalikan "...". Kata kunci ini hanya membantu di tempat ia ditulis, jadi aktifkan noImplicitOverride di tsconfig.json. Setelah itu, setiap method yang meng-override member base class wajib menyatakannya, dan speak() biasa di subclass menjadi error TS4114 (This member must have an 'override' modifier because it overrides a member in the base class 'Animal').

{
    "compilerOptions": {
        "strict": true,
        "noImplicitOverride": true
    }
}

noImplicitOverride bukan bagian dari strict; kamu harus menambahkannya sendiri. override dihapus dari JavaScript hasil kompilasi.

Member protected di Subclass

Member private milik parent tidak terlihat di subclass. Member protected terlihat, dan itulah alasan protected ada: ia adalah API yang ditawarkan class parent kepada turunannya.

Lihat access modifier untuk aturan lengkapnya, termasuk kenapa baik private maupun protected tidak ditegakkan saat runtime.

Mendeklarasikan Ulang Field dengan Tipe yang Lebih Sempit

Subclass sering tahu bahwa sebuah field berisi sesuatu yang lebih spesifik daripada yang dinyatakan parent. Mendeklarasikannya ulang sebagai pet: Dog punya efek saat runtime dengan class field modern: field subclass diinisialisasi menjadi undefined setelah super() selesai, sehingga menghapus nilai yang disimpan constructor parent. Kecuali constructor subclass meng-assign-nya lagi, TypeScript melaporkan ini sebagai TS2612 (Property 'pet' will overwrite the base property in 'Home'). Pakai declare untuk mengubah tipenya saja:

declare tidak menghasilkan kode apa pun, jadi nilai yang diatur constructor Home tetap ada. Constructor yang hanya menerima Dog itulah yang membuat tipe yang lebih sempit tadi benar.

implements vs extends

Kedua kata kunci ini menjawab pertanyaan yang berbeda, dan sebuah class bisa memakai keduanya.

extendsimplements
Dipakai dengansatu classinterface sebanyak apa pun (atau type alias dari tipe object)
Membawa kodeya: field, method, constructortidak, hanya memeriksa bentuk
Ada saat runtimeya, prototype chaintidak, dihapus
instanceof bekerjayatidak
Pemakaian umumberbagi perilaku sungguhanmenjanjikan sebuah bentuk ke bagian kode lainnya

Sebuah class hanya bisa meng-extend satu class (TS1174: Classes can only extend a single class). Untuk menggabungkan perilaku dari beberapa tempat, implement beberapa interface dan bagikan kode lewat komposisi (field yang berisi object helper) atau mixin.

Mixin

Mixin adalah fungsi yang menerima sebuah class dan mengembalikan subclass-nya. Mixin memberi bentuk multiple inheritance yang lolos pemeriksaan tipe:

any[] di Constructor wajib ada: TypeScript hanya menerima base mixin yang constructor-nya menerima satu rest parameter bertipe any[]. Mixin bisa dipakai, tetapi membuat tipe dan stack trace lebih sulit dibaca, jadi coba komposisi lebih dulu.

Kesalahan yang Umum

  • Memanggil method yang bisa di-override dari constructor base class. Constructor base class berjalan sebelum field subclass diinisialisasi, jadi override yang membaca this.items akan melihat undefined, dan pemanggilan seperti this.items.push(x) melempar TypeError saat runtime. TypeScript tidak menangkap kesalahan ini.
  • Lupa memanggil super(...) atau memanggilnya terlambat. Error TS2377 dan TS17009; parent harus membangun object sebelum subclass menyentuh this.
  • Mendeklarasikan ulang field parent tanpa declare. Field subclass akan mengembalikannya ke undefined.
  • Hierarki yang terlalu dalam. Setiap level mengikat turunan pada detail internal parent. Dua level biasanya sudah cukup; lebih dari itu, pilih komposisi atau interface.

Pertanyaan yang sering diajukan

Bagaimana cara kerja inheritance di TypeScript?

Sebuah class mewarisi class lain dengan extends: class Dog extends Animal. Subclass mendapatkan field dan method milik parent, memanggil constructor parent dengan super(...), dan bisa meng-override method. TypeScript memeriksa bahwa override tetap kompatibel tipenya dengan parent.

Apa fungsi kata kunci override di TypeScript?

override menandai method atau properti yang menggantikan member dari base class. Kalau base class tidak punya member dengan nama itu, compiler melaporkan TS4117, sehingga salah ketik dan member yang namanya diubah di parent ketahuan. Dengan noImplicitOverride aktif, setiap override wajib memakai kata kunci itu (kalau tidak, muncul TS4114).

Bagaimana cara memanggil method class parent di TypeScript?

Pakai super.methodName(...) di dalam method subclass. Di dalam constructor, super(...) memanggil constructor parent dan harus dijalankan sebelum this dipakai.

Apakah TypeScript mendukung multiple inheritance?

Tidak. Sebuah class hanya bisa meng-extend satu class (TS1174: Classes can only extend a single class). Class bisa meng-implement interface sebanyak apa pun, dan mixin (fungsi yang menerima class lalu mengembalikan subclass-nya) bisa menggabungkan perilaku dari beberapa sumber.

Apa perbedaan extends dan implements?

extends mewarisi kode sungguhan dari class parent. implements hanya memeriksa bahwa class cocok dengan bentuk sebuah interface dan tidak menambahkan apa pun saat runtime. Class bisa memakai keduanya: class Dog extends Animal implements Pet.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai