Typescript: Mendukung kelas akhir (tidak dapat disubklasifikasikan)

Dibuat pada 26 Apr 2016  ·  124Komentar  ·  Sumber: microsoft/TypeScript

Saya berpikir akan berguna untuk memiliki cara untuk menentukan bahwa suatu kelas tidak boleh disubklasifikasikan, sehingga kompiler akan memperingatkan pengguna pada kompilasi jika melihat kelas lain memperluas yang asli.

Di Java, kelas yang ditandai dengan final tidak dapat diperpanjang, jadi dengan kata kunci yang sama pada TypeScript akan terlihat seperti ini:

final class foo {
    constructor() {
    }
}

class bar extends foo { // Error: foo is final and cannot be extended
    constructor() {
        super();
    }
}
Suggestion Won't Fix

Komentar yang paling membantu

Juga harus ada pengubah final untuk metode:

class Foo {
  final fooIt():void{

  }
}

class Bar {
  fooIt():void {

  }
}
// => Method fooIt of Bar cannot override fooIt of Foo, because it is final.

Misalnya saya sering menggunakan pola berikut, di mana saya ingin segera menghindari fooIt untuk ditimpa:

import Whatever ...

abstract class Foo {
  private ImportantVariable:boolean;

  protected abstract fooIt_inner:Whatever();

  public final fooIt():Whatever() {
    //do somestate change to aprivate member here, which is very crucial for the functionality of every Foo:
    ImportantVariable = true;
    //call the abstract inner functionality:
    return this.fooIt_inner();    
  }
}

Semua 124 komentar

kelas dengan konstruktor pribadi tidak dapat diperpanjang. pertimbangkan untuk menggunakan ini sebagai gantinya.

Dari apa yang saya ingat, saya yakin kompiler tidak menyukai kata kunci pribadi pada konstruktor. Mungkin saya tidak menggunakan versi tempel

Ini adalah fitur baru, akan dirilis di TS 2.0, tetapi Anda dapat mencobanya menggunakan typescript@next . lihat https://github.com/Microsoft/TypeScript/pull/6885 untuk detail lebih lanjut.

Oke terima kasih

Bukankah konstruktor pribadi juga membuat kelas tidak dapat digunakan di luar kelas? Itu bukan jawaban yang tepat untuk kelas akhir.

Java dan/atau C# menggunakan kelas final untuk mengoptimalkan kelas Anda saat runtime, mengetahui bahwa itu tidak akan menjadi khusus. ini menurut saya adalah nilai utama untuk dukungan akhir. Di TypeScript tidak ada yang bisa kami tawarkan untuk membuat kode Anda berjalan lebih baik daripada tanpa final.
Pertimbangkan untuk menggunakan komentar untuk memberi tahu pengguna Anda tentang penggunaan kelas yang benar, dan/atau tidak mengekspos kelas yang Anda inginkan untuk menjadi final, dan sebagai gantinya mengekspos antarmuka mereka.

Saya tidak setuju dengan itu, sebaliknya saya setuju dengan duanyao. Private tidak menyelesaikan masalah itu, karena saya juga ingin kelas yang final dapat dibuat dengan menggunakan konstruktor. Juga tidak memaparkannya kepada pengguna akan memaksa saya untuk menulis pabrik tambahan untuk mereka. Bagi saya, nilai utama dari dukungan akhir adalah mencegah pengguna membuat kesalahan.
Berdebat seperti itu: Apa yang ditawarkan TypeScript untuk membuat kode saya berjalan lebih cepat, ketika saya menggunakan tipe dalam tanda tangan fungsi? Bukankah itu juga hanya untuk mencegah pengguna melakukan kesalahan? Saya bisa menulis komentar yang menjelaskan tipe nilai mana yang harus diteruskan pengguna sebagai parameter. Sayang sekali, bahwa ekstensi seperti kata kunci terakhir dibuang begitu saja, karena menurut saya itu bertabrakan dengan maksud asli dari TypeScript: membuat JavaScript lebih aman dengan menambahkan tingkat kompilasi, yang melakukan pemeriksaan sebanyak mungkin untuk menghindari banyak kesalahan dimuka mungkin. Atau apakah saya salah memahami maksud dari TypeScript?

Juga harus ada pengubah final untuk metode:

class Foo {
  final fooIt():void{

  }
}

class Bar {
  fooIt():void {

  }
}
// => Method fooIt of Bar cannot override fooIt of Foo, because it is final.

Misalnya saya sering menggunakan pola berikut, di mana saya ingin segera menghindari fooIt untuk ditimpa:

import Whatever ...

abstract class Foo {
  private ImportantVariable:boolean;

  protected abstract fooIt_inner:Whatever();

  public final fooIt():Whatever() {
    //do somestate change to aprivate member here, which is very crucial for the functionality of every Foo:
    ImportantVariable = true;
    //call the abstract inner functionality:
    return this.fooIt_inner();    
  }
}

Argumen tentang biaya vs. utilitas adalah argumen yang cukup subjektif. Perhatian utama adalah setiap fitur, konstruksi, atau kata kunci baru menambah kompleksitas pada bahasa dan implementasi kompiler/alat. Apa yang kami coba lakukan dalam pertemuan desain bahasa adalah untuk memahami trade off, dan hanya menambahkan fitur baru ketika nilai tambah melebihi kompleksitas yang diperkenalkan.

Masalah tidak dikunci untuk memungkinkan anggota komunitas terus menambahkan umpan balik. Dengan umpan balik yang cukup dan kasus penggunaan yang menarik, masalah dapat dibuka kembali.

Sebenarnya final adalah konsep yang sangat sederhana, tidak menambah kerumitan bahasa dan harus ditambahkan. Setidaknya untuk metode. Itu menambah nilai, ketika banyak orang mengerjakan proyek besar, sangat berharga untuk tidak mengizinkan seseorang mengganti metode, yang tidak boleh ditimpa.

Di TypeScript tidak ada yang bisa kami tawarkan untuk membuat kode Anda berjalan lebih baik daripada tanpa final.

Wah, ngeri! Tipe statis juga tidak membuat kode Anda berjalan lebih baik, tetapi keamanan adalah hal yang menyenangkan untuk dimiliki.

Final (disegel) ada di sana dengan penggantian sebagai fitur yang ingin saya lihat untuk membuat penyesuaian kelas sedikit lebih aman. Saya tidak peduli dengan kinerja.

Tipe statis juga tidak membuat kode Anda berjalan lebih baik, tetapi keamanan adalah hal yang menyenangkan untuk dimiliki.

Tepat. Sama seperti private mencegah orang lain memanggil metode, final membatasi orang lain untuk mengganti metode.

Keduanya adalah bagian dari antarmuka OO kelas dengan dunia luar.

Sepenuhnya setuju dengan @pauldraper dan @mindarelus. Tolong terapkan ini, ini akan sangat masuk akal saya sangat merindukannya saat ini.

Saya tidak berpikir final hanya bermanfaat untuk kinerja, itu juga bermanfaat untuk desain tetapi saya tidak berpikir itu masuk akal dalam TypeScript sama sekali. Saya pikir ini lebih baik diselesaikan dengan melacak efek mutabilitas Object.freeze dan Object.seal .

@aluanhaddad Bisakah Anda menjelaskannya lebih detail? Mengapa menurut Anda itu tidak "masuk akal dalam TypeScript sama sekali"?
Membekukan atau menyegel objek berarti melarang penambahan properti baru ke objek, tetapi tidak mencegah penambahan properti ke objek turunan, jadi meskipun saya akan menyegel kelas dasar, saya masih bisa mengganti metode di kelas anak, yang memperluas kelas dasar itu . Plus saya tidak bisa menambahkan properti apa pun ke kelas dasar saat runtime.

Gagasan menggunakan final pada kelas atau metode kelas di Jawa lebih berkaitan dengan meminimalkan mutabilitas objek untuk keamanan utas menurut saya. (Item 15. Joshua Bloch, Java Efektif)

Saya tidak tahu apakah prinsip-prinsip ini terbawa ke javascript mengingat semua yang ada di JS bisa berubah (koreksi saya jika saya salah). Tapi TypeScript bukan Javascript, ya?

Saya sangat ingin melihat ini diterapkan. Saya pikir itu akan membantu membuat kode yang lebih kuat. Sekarang... Bagaimana itu diterjemahkan ke dalam JS, sejujurnya mungkin tidak perlu. Itu bisa tetap berada di sisi TypeScript dari pagar di mana sisa pemeriksaan waktu kompilasi kami berada.

Tentu saya bisa hidup tanpanya, tapi itu bagian dari TypeScript, kan? Memeriksa ulang kesalahan kita yang diabaikan?

Bagi saya final akan memainkan peran yang sama dalam TypeScript sebagai private atau mengetik, yaitu kontrak kode. Mereka dapat digunakan untuk memastikan kontrak kode Anda tidak rusak. Saya sangat menyukainya.

@hk0i juga disebutkan dalam Item 17 (edisi ke-2) dengan cara yang mirip dengan apa yang digaungkan di sini:

Tapi bagaimana dengan kelas beton biasa? Secara tradisional, mereka tidak final atau dirancang dan didokumentasikan untuk subclassing, tetapi keadaan ini berbahaya. Setiap kali perubahan dibuat di kelas seperti itu, ada kemungkinan kelas klien yang memperpanjang kelas akan rusak. Ini bukan hanya masalah teoretis. Tidak jarang menerima laporan bug terkait subkelas setelah memodifikasi internal kelas beton nonfinal yang tidak dirancang dan didokumentasikan untuk pewarisan.

Solusi terbaik untuk masalah ini adalah melarang subkelas di kelas yang tidak dirancang dan didokumentasikan untuk disubkelaskan dengan aman. Ada dua cara untuk melarang subclassing. Yang lebih mudah dari keduanya adalah mendeklarasikan final kelas. Alternatifnya adalah menjadikan semua konstruktor pribadi atau paket-pribadi dan menambahkan pabrik statis publik di tempat konstruktor.

Saya berpendapat itu tidak meningkatkan kompleksitas kognitif bahasa mengingat kata kunci abstrak sudah ada. Namun, saya tidak dapat berbicara tentang dampak implementasi/kinerjanya dan sangat menghormati melindungi bahasa dari sudut itu. Saya pikir memisahkan kekhawatiran itu akan bermanfaat untuk memutuskan apakah akan menerapkan fitur ini atau tidak.

Saya percaya bahwa final akan menjadi tambahan yang bagus untuk menyegel kelas. Salah satu kasus penggunaan adalah bahwa Anda mungkin memiliki banyak metode publik di kelas Anda tetapi mengekspos, melalui antarmuka, hanya sebagian dari mereka. Anda dapat menguji unit implementasi dengan cepat karena memiliki semua metode publik ini sementara konsumen nyata menggunakan antarmuka untuk membatasi akses ke metode tersebut. Mampu menutup implementasi akan memastikan bahwa tidak ada yang memperpanjang implementasi atau mengubah metode publik.

Anda juga dapat memastikan bahwa tidak ada yang mewarisi kelas Anda. TypeScript harus ada di sana untuk menegakkan aturan itu, dan saran tentang berkomentar tampaknya merupakan pendekatan yang malas untuk menyelesaikan kasus penggunaan ini. Jawaban lain yang saya baca adalah tentang penggunaan pribadi yang hanya cocok untuk situasi tertentu tetapi bukan yang saya jelaskan di atas.

Seperti banyak orang di utas ini, saya akan memilih untuk dapat menyegel kelas.

@mhegazy Konstruktor pribadi dan kelas tersegel/final memiliki semantik yang sangat berbeda. Tentu, saya dapat mencegah ekstensi dengan mendefinisikan konstruktor pribadi, tetapi kemudian saya juga tidak dapat memanggil konstruktor dari luar kelas, yang berarti saya kemudian perlu mendefinisikan fungsi statis untuk memungkinkan instance dibuat.

Secara pribadi saya menganjurkan untuk memiliki pemeriksaan kompiler tersegel/akhir untuk memastikan bahwa kelas dan metode yang ditandai disegel/final tidak dapat diperpanjang atau diganti.

Dalam konteks masalah saya, saya ingin dapat memiliki konstruktor publik sehingga saya dapat membuat instance objek, tetapi mencegah ekstensi kelas, dan hanya penambahan tersegel/final yang memungkinkan itu.

Ada tugas - tulis kode itu

  • mengoperasikan objek yang tidak dapat diubah
  • bebas warisan
  • metode refleksi gratis

Dan kata kunci final diperlukan di sini, IMHO.

Saya melihat final sebagai cara yang bagus untuk mengingatkan diri Anda dan pengguna kode Anda tentang metode terproteksi mana yang harus mereka timpa, dan mana yang tidak. Sebagai catatan tambahan, saya juga pendukung untuk secara eksplisit menyatakan bahwa Anda mengesampingkan, sebagian agar pembaca tahu, tetapi juga agar transpiler mengeluh jika Anda salah ketik nama metode.

@zigszigsdk Karena metode tidak pernah diganti, bagaimana cara kerjanya?

Untuk final :
Cara yang sama seperti cara kerjanya sekarang, kecuali transpiler akan mengeluh jika seseorang menyembunyikan metode super -yang telah dinyatakan final- dari konteks this .

Untuk override :
Cara kerjanya sama sekarang, kecuali transpiler akan mengeluh jika seseorang mendeklarasikan metode override , dan supernya tidak memiliki metode dengan nama itu, atau memang memilikinya tetapi dinyatakan sebagai final .
Mungkin juga memperingatkan Anda jika Anda menyembunyikan metode super dari konteks this dan tidak menyatakan penggantian.

Java dan/atau C# menggunakan kelas terakhir untuk mengoptimalkan kelas Anda saat runtime, mengetahui bahwa itu tidak akan menjadi khusus. ini menurut saya adalah nilai utama untuk dukungan akhir.

@mhegazy Tidak cukup! Fungsi penting lainnya adalah menjadi eksplisit tentang bagian mana dari kelas yang diharapkan untuk ditimpa.

Misalnya, lihat item 17 di "Java Efektif": "Desain dan dokumen untuk warisan atau yang lain melarangnya":

Tidak jarang menerima laporan bug terkait subkelas setelah memodifikasi internal kelas beton nonfinal yang tidak dirancang dan didokumentasikan untuk pewarisan. Solusi terbaik untuk masalah ini adalah melarang subkelas di kelas yang tidak dirancang dan didokumentasikan untuk disubkelaskan dengan aman. Ada dua cara untuk melarang subclassing. Yang lebih mudah dari keduanya adalah mendeklarasikan final kelas.

Hal yang sama berlaku untuk metode terakhir, menurut saya. Sangat jarang bahwa suatu kelas dirancang untuk mendukung penggantian salah satu metode publiknya. Ini akan membutuhkan desain yang sangat rumit dan sejumlah besar pengujian, karena Anda harus memikirkan setiap kemungkinan kombinasi status yang mungkin dihasilkan dari kombinasi perilaku yang tidak diganti dan yang mengesampingkan. Jadi sebagai gantinya seorang programmer mungkin mendeklarasikan semua metode publik final kecuali satu atau dua, yang akan mengurangi jumlah kombinasi tersebut secara dramatis. Dan lebih seringnya tidak, satu atau dua metode yang dapat ditimpa justru yang dibutuhkan.

Saya pikir kelas dan metode final sangat penting. Saya harap Anda akan menerapkannya.

Kasus pengguna lain yang menarik @mhegazy . Hari ini saya mempelajari Template Method Pattern , dan menemukan bahwa final diperlukan untuk melarang subclass mengubah metode template seperti yang dikatakan oleh Template Method Pattern di wikipedia . Tapi saya tidak bisa melakukan ini di TypeScript saya yang cantik. Sayang sekali!

Pola template memungkinkan subkelas mendefinisikan kembali langkah-langkah tertentu dari suatu algoritma tanpa mengubah struktur algoritma

Bagi saya itu sesederhana mencoba menegakkan komposisi dalam kasus pewarisan. Tanpa final Anda tidak dapat melakukan ini.

Sebanyak saya juga ingin melihat final datang ke TypeScript, saya pikir pesan mendasar yang coba dikirim Microsoft kepada kami adalah bahwa jika kami ingin final , kami harus menggunakan bahasa yang mendukungnya.

Saya ingin melihat masalah ini dibuka kembali dan diimplementasikan.

Saat membangun perpustakaan yang akan Anda gunakan secara internal atau bagikan secara publik (seperti di npm), Anda tidak ingin mereka yang menggunakannya harus menelusuri kode dan menelusuri komentar atau dokumen untuk melihat apakah kelas dapat/bisa ' tidak akan ditimpa. Jika mereka menimpanya menimbulkan kesalahan.

Dalam kode saya, saya memiliki kelas yang diperluas, dan ketika semacam peristiwa terjadi, itu memicu sebuah metode. Jika sub-kelas mendefinisikan metode itu akan yang itu jika tidak maka akan kembali ke yang default. Namun ada juga metode lain di kelas yang bukan metode "fallback", mereka lebih merupakan metode pembantu seperti menambahkan item ke DOM dalam hal ini saya tidak ingin metode ini ditimpa.

5 sen saya tentang topik ini adalah bahwa ini harus dibuka kembali dan diimplementasikan.

Saya akan menggunakannya untuk memaksa klien perpustakaan untuk membuat kelas konkret mereka sendiri dan tidak meluas dari beberapa yang sudah ada. Seperti yang dikatakan @lucasyvas ,

Saya juga memilih untuk membuka kembali, dengan dukungan untuk kedua kelas dan metode

Saya tidak mengatakan bahwa kita tidak seharusnya Saya hanya tidak yakin bagaimana pola kata kunci terakhir akan bekerja dalam konteks TypeScript?

bukankah kata kunci terakhir akan menghentikan pengguna/programmer untuk menimpa/mewarisi dari kelas/metode tersebut?

Cara saya melihat Final adalah "Membekukan" data yang berarti tidak dapat diubah lagi. Maksud saya ini bisa menyenangkan untuk digunakan, meskipun bisa benar-benar merepotkan bagi orang-orang yang ingin "memperbaiki" atau mengubah beberapa perilaku.

@niokasgami Freeze terkait dengan [data] (https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/freeze)
dan sudah tersedia sebagai bagian dari ES6. Final berlaku untuk kelas dan metode (variabel hanya dapat dibaca dan oleh karena itu bertindak sebagai "final" karena kami tidak dapat mengganti variabel super).

Kata kunci "Final" digunakan di JAVA di mana disegel dalam C# .

Karena TypeScript sangat terinspirasi oleh C#, seharusnya ada kemungkinan lebih tinggi bahwa "disegel" akan menjadi yang terpilih. Sekarang, kita mungkin bingung dengan segel ES6 yang pada dasarnya menyegel objek saat runtime?

@Xample Saya pikir Anda juga dapat menggunakan final pada variabel lokal di Jawa. Saya ingat harus melakukannya ketika saya sedang menulis aplikasi android. Saya pikir itu mengharuskan saya untuk melakukannya dengan sebuah acara tetapi saya tidak 100% yakin.

function(){
    let final myVariable = 'Awesome!'
    document.addEventListener('scroll', e => {
        console.log(myVariable)
        myVariable = 'Not Awesome' // Throws an error
    })
}

@TheColorRed Nah, variabel lokal terakhir di Jawa berarti konstan, bukan?

@EternalPhane Saya kira begitu, saya selalu menggunakan const dalam lingkup seperti global... Tidak pernah berpikir untuk menggunakannya dalam suatu fungsi atau metode... Saya kira saya komentar sebelumnya batal kemudian...

@Xample Ya, saya mengerti maksud Anda untuk Sealed from the C # itu tidak berfungsi sama Jujur menurut saya KEDUA kata kunci seal dan final tidak dapat benar-benar masuk kategori karena keduanya dapat membingungkan (disegel untuk seal dll) dan Final kami tidak yakin bagaimana menerapkannya karena di Jawa digunakan sebagai konstanta:/

Di java Anda dapat final variabel, metode, atau kelas apa pun. Untuk variabel, ini berarti referensi tidak dapat dipindahkan (untuk tipe non-referensi, ini berarti Anda juga tidak dapat mengubah nilainya). Untuk kelas itu berarti tidak ada ekstensi ( extends ... kata kunci). Untuk metode itu berarti tidak ada override di subclass.

Alasan utama untuk final di java:

  • Kurangi kekekalan variabel: untuk keamanan utas
  • Selesaikan kelas sehingga tidak dapat diwarisi: memaksakan komposisi di atas pewarisan karena pewarisan secara inheren rumit ketika subkelas Anda mulai secara tidak sengaja memanggil metode super
  • Mencegah kesalahan pemrograman sederhana: jika Anda tidak berniat mengubah nilai, kabar baiknya itu tidak mungkin lagi
  • Digunakan untuk apa yang kebanyakan orang pikirkan ketika mereka membayangkan konstanta: nilai yang dapat digunakan kembali yang akan Anda lihat di mana-mana, seperti pada public static final String KEY = "someStringKeyPathHere" , berguna untuk mengurangi kesalahan tetapi memiliki manfaat waktu kompilasi tambahan karena tidak membuat ulang beberapa objek string untuk nilai yang sama

Saya mungkin telah meninggalkan beberapa kasus penggunaan tetapi saya mencoba untuk membuatnya sangat umum dan memberikan beberapa contoh.

@TheColorRed Android memerlukannya saat Anda mendeklarasikan variabel di luar panggilan balik (kelas anonim) dan kemudian mereferensikannya di dalam. Saya pikir ini sejalan dengan masalah keamanan utas lagi. Saya pikir mereka mencoba menghentikan Anda untuk menetapkan kembali variabel dari utas lain.

Terlepas dari kegunaannya, saya tidak berpikir kita akan melihat fitur ini datang dalam waktu dekat. Mungkin ini saat yang tepat untuk melihat solusi lain atau, seperti dalam kasus saya, lepaskan TypeScript bersama-sama dan atasi kecerobohan di JS. Javascript memiliki banyak "manfaat" dengan tidak memiliki petunjuk jenis ini atau apa pun dan pada akhirnya kode Anda akan dikonversi ke JS. Jika Anda menginginkan kompatibilitas browser, Anda dapat menulis ES6 dan menggunakan Babel untuk mengonversinya.

Saya pikir secara realistis meskipun Anda dapat memberikan tamparan di pergelangan tangan untuk beberapa manfaat ini, Anda tidak mendapatkan manfaat nyata dari java-esque final setelah dikonversi ke js.

@hk0i Saya pikir saya bisa mengerti maksud Anda tetapi secara keseluruhan saya setuju dan agak tidak setuju pada saat yang sama tentang pendapat Anda.

Sebagian besar kode dalam TypeScript adalah saya akan mengatakan untuk tujuan debugging/pelacakan untuk membantu programmer dengan baik Saya kira melakukan kode "bersih" (tolong jangan mengutip saya bahwa saya tahu mereka memiliki beberapa kode spaghetti TypeScript di sini hahah)

sebagian besar fitur TypeScript tidak terhapus sebagian besar waktu ketika dikonversi ke JS karena mereka tidak ada di JS.

Meskipun ketika Anda menjalankan/menulis kode Anda, itu membantu untuk melihat kesalahan dalam kode Anda dan melacaknya lebih mudah daripada dengan JS (dan HECK JS bisa sulit untuk melacak bug lol)

Jadi saya pikir penggunaan kata kunci final dapat dilihat sebagai pemblokir bagi pengguna API untuk mengetahui: Oh oke kelas ini tidak boleh diperpanjang karena ekstensinya dapat memicu perilaku aneh dll ...

TypeScript sebagai bahasa yang baik. Saya merasa sedih karena tidak memiliki beberapa fitur penting seperti kata kunci kelas statis "benar" yang menghentikan orang memanggil konstruktor kelas tetapi masih dapat mengaksesnya.

@hk0i Saya juga tidak setuju, tujuan TypeScript adalah untuk menyusun kode dan karena itu menyediakan alat untuk melakukannya. Kami tidak HARUS menggunakan final di Java, kami biasanya menggunakannya ketika kami tahu bahwa memperluas kelas atau metode dapat menyebabkan pengenalan efek samping (atau untuk alasan keamanan lainnya).

Mengandalkan solusi ES6 saat ini menambahkan pemeriksaan mutabilitas runtime (mengatakan menggunakan Object.seal atau Object.freeze) membuat Anda kehilangan manfaat dari kesalahan secara langsung saat Anda mengetik kode Anda (meskipun itu dapat dengan mudah dilakukan menggunakan dekorator ini ).

@TheColorRed ya… dan dalam TypeScript kami memiliki perbedaan untuk variabel lokal dan kelas.

  • readonly untuk variabel kelas
  • const : untuk variabel lain

Tak satu pun dari mereka akan akurat untuk metode atau kelas. Mengganti metode tidak berarti kita mendefinisikan ulang (pikirkan dalam metode virtual di C++), kita hanya mendefinisikan sebuah metode akan menjadi yang utama dipanggil sebelum yang super (yang dapat kita panggil menggunakan super ).

@niokasgami mungkin penamaannya tidak terlalu menjadi masalah karena kita masih akan mengerti bahwa Object.seal diterapkan ke… objek sambil menyegel kelas dan metode ke struktur itu sendiri.

TL; DR:

  • preventExtensions akan menjadi nama eksplisit yang bagus untuk mencegah perluasan kelas
  • preventOverrides akan menjadi nama kandidat yang baik untuk mencegah kelas override
    TAPI: seal banyak digunakan di C#, lebih pendek dan memiliki perilaku yang diharapkan sama.

Omong-omong saya lupa menyebutkan ada juga Object.preventExtensions()
Perbedaannya bisa dilihat disini

membaca

  • selalu mungkin… siapa yang ingin variabel tulis saja?

memperbarui dan menghapus

  • mencegah mendefinisikan ulang menghapus variabel: readonly untuk anggota kelas, const (untuk yang lain)
  • mencegah mendefinisikan ulang metode: tidak ada yang mencegah Anda untuk melakukan (dalam metode kelas) this.aMethod = ()=>{} . Haruskah metode readonly juga untuk mencegah peretasan seperti itu? sama untuk delete this.aMethod

menciptakan

  • hanya berlaku untuk objek atau seperti yang kita bicarakan di sini tentang struktur: class sebagai kelas mendefinisikan anggota objek, TypeScript secara implisit sudah mencegah pembuatan properti baru dengan cepat… misalnya this.unKnownProperty = "something" ts akan memunculkan kesalahan waktu kompilasi TS2339 seperti yang diharapkan.

memperluas

  • hanya berlaku untuk kelas, kami belum melakukan apa pun pada objek itu sendiri… (tidak seperti pembuatan yang saat runtime). Di sinilah final atau seal akan relevan. Pertanyaannya adalah bagaimana tetap konsisten dengan pembekuan, segel dan preventExtensions dari ES6 ? (jika kita harus).
    Memperluas kelas, berarti kita mencegah pembuatan kelas lain dari kelas ini. Kami kemudian dapat membacanya tetapi tidak menghapusnya (yang tetap menghapus kelas). Pertanyaannya bukan kolom "perbarui". Apakah kelas final dapat diperbarui? Apa itu kelas yang dapat diperbarui? Dugaan saya adalah ya… tetapi kemudian pembaruannya adalah mengimplementasikan kelas ini (memahami antarmuka kelas)? Menerapkan kelas lain harus selalu memungkinkan… tetapi itu tidak terkait dengan memperbarui kelas awal. Yah… mungkin mereka hanya sampai pada pertanyaan yang sama dengan menulis c# dan memutuskan bahwa seal adalah kata kunci yang baik.

utama

  • mencegah menimpa metode saat ini di kelas yang diperluas. Sekali lagi, kami belum menimpa apa pun saat kami berbicara tentang definisi (metode di dalam kelas). Jadi cegah pembuatan, izinkan membaca, izinkan pembaruan (kami dapat mencegah pembaruan metode atau menghapusnya menggunakan readonly jika itu berlaku untuk metode juga). Penamaan terbaik? preventOverrides ? atau lagi seal seperti yang digunakan dalam C#

BONUS: Karena menyegel metode biasanya untuk mencegah orang mengesampingkan kode super wajib, saya membuat proposal untuk memaksa orang memanggil metode super (perilaku yang sama seperti untuk konstruktor tetapi untuk metode apa pun) jangan ragu untuk memilih jika Anda pikir itu akan berguna.

Jangan salah paham, saya tidak menentang ini. Saya sangat mendukungnya. Saya hanya mencoba untuk mendukung pola pikir paradoks Microsoft tentang mengapa itu tidak boleh menjadi fitur, yang pada dasarnya mengatakan bahwa karena itu bukan fitur JavaScript, itu tidak bisa menjadi fitur TypeScript.

... Jika saya mengerti dengan benar.

@hk0i Generik, readonly, dan banyak fitur lainnya bukan fitur JavaScript, tetapi masih ditambahkan ke TypeScript, jadi saya rasa bukan itu alasannya...

TypeScript hanya menghindar dari fitur yang membutuhkan pembuatan kode dengan cara yang tidak langsung. Generik, readonly, dll adalah gagasan waktu kompilasi murni yang dapat dengan mudah dihapus untuk menghasilkan output yang dihasilkan, jadi itu adalah permainan yang adil.

final dapat "dihapus" pada waktu kompilasi juga, jadi mengapa itu tidak menjadikannya "permainan yang adil"?

@TheColorRed Saya pikir ini kurang lebih ini baik mereka memiliki prioritas lain untuk diintegrasikan.
Atau mereka tidak yakin seberapa dapat diprediksi kata kunci baru ini dapat bekerja pada waktu kompilasi/pengkodean. jika Anda memikirkannya, desain kata kunci akhir bisa sangat kabur. final dapat digunakan banyak untuk berbagai tujuan. Seperti yang Anda lihat dari dokumentasi Jsdoc, Anda dapat menggunakan tag "@final" yang memberi tahu juru bahasa jsdoc bahwa variabel ini dibekukan sehingga hanya dapat dibaca.

di sini bentrokan desain. Readonly berlaku pada variabel dan pengambil jika saya mengingat variabel mana yang "membekukan" dan membuatnya hanya-baca dan final membuatnya final sehingga hanya-baca juga.

ini dapat menyebabkan kebingungan bagi programmer yang tidak yakin apakah mereka harus menandai variabel mereka sebagai final atau hanya-baca.

KECUALI kami memesan kata kunci ini secara eksklusif untuk deklarasi kelas dan metode, saya pikir perilaku atau final akan berbenturan dengan readonly.

Saya pikir alih-alih disegel (yang dapat berbenturan dengan object.seal) atau final (yang tujuan ini mendesain kata kunci hanya baca bentrokan) kita dapat menggunakan cara yang lebih "langsung" untuk memberi nama dan mendesainnya.

perhatikan ini hanya "gagasan" tentang perilaku yang serupa tetapi pada saat yang sama berbeda dari perilaku C #? Saya mengambil beberapa inspirasi dari bagaimana C# biasanya nonOverridable dan kemudian Anda harus mengatakan bahwa metode ini virtual.

` ruang nama Contoh {

export class myClass {

    constructor(){}

    // Make the func non ovveridable by inheritance. 
    public unoveridable myMethod(){

    }

    public myMethodB(){

    }
}

export class MyClassB extends myClass {

    // Nope not working will throw an error of an nonOverridable error
    myMethod(){
        super.myMethod();
    }

    // Nope will not work since unoverridable.
    myMethod(){

    }

    // Design could be implemented differently since this could complicate  ¸
    // the pattern of typescript
    public forceoverride myMethod(){
      // destroy previous pattern and ignore the parent method thus a true ovveride
    }

    // Yup will work since it's still "virtual".
    myMethodB(){
        super.myMethodB();
    }
}
// Can extend an existing Parent class
// Declaring an unextendable class make all is component / func nonOverridable by 
// inheritance. (A class component can still be overwritten by prototype usage.)
export unextendable class MyFinalClass extends Parent {

    constructor()

    // nonoverridable by default.
    public MyMethod(){

    }
}
// nope throw error that MyFinalClass is locked and cannot be extended
export class MyChildClass extends MyFinalClass{}

}`

Sunting: Saya tidak menyertakan Variabel dan dapatkan karena hanya baca sudah ada.

@mhegazy Harap pertimbangkan untuk membuka kembali masalah ini. Tanggapan komunitas tampaknya sangat terkait dengan penambahan kata kunci final , seperti Java ( final ), C# ( sealed , readonly ), PHP ( final ), Scala ( final , sealed ), dan C++ ( final , virtual ) miliki. Saya tidak dapat memikirkan bahasa OOP yang diketik secara statis yang tidak memiliki fitur ini, dan saya belum mendengar argumen yang meyakinkan mengapa TS tidak membutuhkannya.

@bcherny @mhegazy Saya sepenuhnya setuju dengan hal di atas. Saya tidak dapat melihat alasan yang diperdebatkan dengan baik mengapa final tidak bisa hanya menjadi batasan waktu kompilasi lainnya, seperti kebanyakan gula sintaksis yang kita lihat di TypeScript - hadapi saja, pengetikan statis, generik, pengubah akses, alias ketik , antarmuka...SEMUA GULA! Saya mengerti bahwa tim TypeScript mungkin ingin fokus untuk mengambil bahasa ke arah lain, tapi serius, ini OOP 101... ini seharusnya sudah lama sekali, ketika TypeScript masih sangat muda!

@mhegazy bolehkah saya merujuk Anda kembali ke komentar awal yang Anda buat tentang masalah ini?

kelas dengan konstruktor pribadi tidak dapat diperpanjang. pertimbangkan untuk menggunakan ini sebagai gantinya.

Pada saat penulisan, komentar ini telah diturunkan sebanyak 21 kali.
Jelas ini BUKAN YANG DIINGINKAN MASYARAKAT SEBAGAI SOLUSI!

wow, mendapat banyak masukan tentang ini. Saya tidak mengerti mengapa tidak ada minat untuk diterapkan

Banyak keluhan yang benar-benar tidak produktif terjadi di sini. Tolong jangan hanya muncul untuk menyuarakan frustrasi - kami diizinkan untuk membuat keputusan di sini dan komunitas diizinkan untuk memberikan umpan balik mereka, tetapi hanya menggertakkan gigi tidak akan mengubah pikiran siapa pun. Harap spesifik dan dapat ditindaklanjuti; kami tidak akan diyakinkan oleh orang yang hanya muncul untuk mengatakan LAKUKAN FITUR INI SEKARANG .

Perhatikan bahwa masalah ini adalah tentang kelas akhir/tertutup . Ada diskusi di luar topik tentang metode akhir/tersegel yang merupakan konsep yang sama sekali berbeda.

Beberapa poin dipertimbangkan ketika kami meninjau ini terakhir kali

  • KELAS ADALAH FITUR JAVASCRIPT, BUKAN FITUR TYPESCRIPT . Jika Anda benar-benar merasa bahwa kelas sama sekali tidak berguna tanpa final , itu adalah sesuatu untuk dibicarakan dengan komite TC39 atau dibawa ke es-discuss. Jika tidak ada yang mau atau mampu meyakinkan TC39 bahwa final adalah fitur yang harus dimiliki, maka kita dapat mempertimbangkan untuk menambahkannya ke TypeScript.
  • Jika legal untuk memanggil new pada sesuatu, itu memiliki korespondensi runtime satu-ke-satu dengan mewarisi darinya. Gagasan tentang sesuatu yang bisa new 'd tetapi tidak super 'd tidak ada dalam JavaScript
  • Kami mencoba yang terbaik untuk menghindari mengubah TypeScript menjadi sup kata kunci OOP. Ada banyak mekanisme komposisi yang lebih baik daripada pewarisan dalam JavaScript dan kita tidak perlu memiliki setiap kata kunci yang dimiliki C# dan Java.
  • Anda dapat dengan mudah menulis pemeriksaan runtime untuk mendeteksi pewarisan dari kelas "seharusnya menjadi final", yang tetap harus Anda lakukan jika Anda "khawatir" tentang seseorang yang secara tidak sengaja mewarisi dari kelas Anda karena tidak semua konsumen Anda mungkin menggunakan TypeScript .
  • Anda dapat menulis aturan lint untuk menerapkan konvensi gaya yang tidak diwarisi dari kelas tertentu
  • Skenario bahwa seseorang akan "secara tidak sengaja" mewarisi dari kelas Anda yang harus disegel agak aneh. Saya juga berpendapat bahwa penilaian individu seseorang tentang apakah "mungkin" untuk mewarisi dari kelas tertentu mungkin agak mencurigakan.
  • Pada dasarnya ada tiga alasan untuk menyegel kelas: Keamanan, kinerja, dan niat. Dua di antaranya tidak masuk akal dalam TypeScript, jadi kita harus mempertimbangkan kompleksitas vs keuntungan dari sesuatu yang hanya melakukan 1/3 pekerjaan yang dilakukannya di runtime lain.

Anda bebas untuk tidak setuju dengan semua ini, tetapi harap berikan umpan balik yang dapat ditindaklanjuti dan spesifik tentang bagaimana kurangnya final membahayakan kemampuan Anda untuk menggunakan kelas JavaScript secara efektif.

belum mendengar argumen yang meyakinkan mengapa TS tidak membutuhkannya

Sekedar mengingatkan bahwa ini bukan cara kerjanya. Semua fitur mulai dari -100 . Dari postingan itu

Dalam beberapa pertanyaan tersebut, pertanyaan tersebut menanyakan mengapa kita “mengeluarkan” atau “meninggalkan” fitur tertentu. Kata-kata itu menyiratkan bahwa kami memulai dengan bahasa yang ada (C++ dan Java adalah pilihan populer di sini), dan kemudian mulai menghapus fitur hingga kami mencapai titik yang kami sukai. Dan, meskipun mungkin sulit bagi sebagian orang untuk percaya, bukan itu cara bahasa dirancang.

Kami memiliki anggaran kompleksitas yang terbatas. Semua yang kami lakukan membuat setiap hal berikutnya yang kami lakukan 0,1% lebih lambat. Saat Anda meminta fitur, Anda meminta agar setiap fitur lainnya menjadi sedikit lebih sulit, sedikit lebih lambat, sedikit lebih mungkin. Tidak ada yang masuk ke sini karena Java memilikinya atau karena C# memilikinya atau karena hanya beberapa baris kode atau Anda dapat menambahkan switch baris perintah . Fitur perlu membayar untuk diri mereka sendiri dan untuk seluruh beban mereka di masa depan.

@RyanCavanaugh Meskipun saya tidak yakin saya setuju dengan keputusan itu, saya menghargai tanggapan yang sangat bijaksana dan terperinci, dan atas waktu Anda untuk menulisnya. Saya mengerti bahwa Anda ingin menjaga bahasa tetap ramping - ada baiknya untuk merenungkan apakah fitur yang dimiliki bahasa populer lainnya benar-benar berharga, atau hanya umum.

@RyanCavanaugh

Saya tidak berpikir intinya di sini, di forum ini adalah .. "menggunakan kelas JavaScript secara efektif."

Maaf, saya tidak bisa menolak, dan hanya menambahkan "keluhan tidak produktif yang terjadi di sini": kata kunci terakhir berguna untuk semua alasan yang dijelaskan, secara rinci, dan dengan argumen oleh orang lain.

Kami mencoba yang terbaik untuk menghindari mengubah TypeScript menjadi sup kata kunci OOP. Ada banyak mekanisme komposisi yang lebih baik daripada pewarisan dalam JavaScript...

Seperti ketika Anda menggunakan final untuk menegakkan komposisi atas pewarisan di (masukkan bukan nama bahasa TypeScript di sini)
(Maaf, saya tidak bisa menolak)

Tetapi lebih penting lagi, sepertinya TS bermaksud untuk sebagian besar menjadi lapisan kompatibilitas untuk memperkenalkan "JavaScript hari ini sekarang" atau sesuatu untuk efek itu (a la Babel) dengan pengecualian bahwa ia menambahkan beberapa pemeriksaan tipe tambahan.

Saya pikir inti dari argumen kontra untuk mengimplementasikan ini adalah bahwa jika Anda menginginkan fitur bahasa lain, gunakan bahasa lain sebagai gantinya. Jika Anda ingin JavaScript, gunakan JavaScript. Jika Anda ingin TypeScript, gunakan itu.

Saya tahu Kotlin dapat dikompilasi ke JS dan memiliki banyak fitur yang menurut saya akan dinikmati oleh orang-orang di sini, jadi itu adalah sesuatu yang mungkin berguna untuk dilihat. Saya pikir itu memang menambahkan beberapa perpustakaan JS sebagai ketergantungan (saya belum mencobanya sendiri).

Takeaway utama adalah bahwa jika Anda tidak senang dengan TypeScript, jangan gunakan itu.

@RyanCavanaugh

Pada poin yang Anda kemukakan di atas ...

KELAS ADALAH FITUR JAVASCRIPT, BUKAN FITUR TYPESCRIPT . Jika Anda benar-benar merasa bahwa kelas sama sekali tidak berguna tanpa final , itu adalah sesuatu untuk dibicarakan dengan komite TC39 atau dibawa ke es-discuss. Jika tidak ada yang mau atau mampu meyakinkan TC39 bahwa final adalah fitur yang harus dimiliki, maka kita dapat mempertimbangkan untuk menambahkannya ke TypeScript.

Saya telah mengikuti TypeScript sejak 0,7 dan saat itu, IIRC menargetkan ES3-ES5. Kelas jelas merupakan fitur TypeScript saat itu. Argumen yang sama berlaku untuk dekorator - mereka ada di kartu untuk ES, tetapi mereka sudah ada di sini di TypeScript sebagai fitur eksperimental.

Sebagai sebuah komunitas, kami tidak berargumen bahwa kompiler TypeScript entah bagaimana harus memancarkan kelas JavaScript yang final . Pemeriksaan waktu kompilasi sudah cukup, dengan cara yang sama seperti pemeriksaan waktu kompilasi untuk pengubah akses sudah cukup; lagi pula, itu juga bukan fitur JavaScript, tetapi TypeScript masih memungkinkannya.

Jika final memang menjadi fitur ES, maka mungkin Anda dapat memfaktorkan ulang bit emitor itu saat menargetkan ES* untuk menghasilkan final tepat (Sama seperti yang Anda lakukan untuk kelas, saat diperkenalkan di ES6)?

Jika legal untuk memanggil new pada sesuatu, itu memiliki korespondensi runtime satu-ke-satu dengan mewarisi darinya. Gagasan tentang sesuatu yang bisa new 'd tetapi tidak super 'd tidak ada dalam JavaScript

Sekali lagi, saya tidak berpikir komunitas mengharapkan Anda untuk membuat sesuatu di sini untuk menegakkan final saat runtime. Seperti yang telah Anda nyatakan secara akurat, itu "tidak ada dalam JavaScript", tetapi seperti yang disebutkan, batasan waktu kompilasi sudah cukup.

Kami mencoba yang terbaik untuk menghindari mengubah TypeScript menjadi sup kata kunci OOP. Ada banyak mekanisme komposisi yang lebih baik daripada pewarisan dalam JavaScript dan kita tidak perlu memiliki setiap kata kunci yang dimiliki C# dan Java.

Ini adalah ungkapan yang cukup terkenal di komunitas pengembangan perangkat lunak secara keseluruhan: "Mendukung komposisi daripada warisan", namun "mendukung" tidak berarti tidak menggunakan warisan sebagai pernyataan menyeluruh. Tentu, Anda tidak _memerlukan_ setiap kata kunci yang dimiliki C# dan Java, tetapi Anda harus memiliki alasan untuk mengizinkan kelas abstract (yang omong-omong "tidak ada dalam JavaScript"), jadi mengapa tidak final kelas?

Anda dapat dengan mudah menulis pemeriksaan runtime untuk mendeteksi pewarisan dari kelas "seharusnya menjadi final", yang tetap harus Anda lakukan jika Anda "khawatir" tentang seseorang yang secara tidak sengaja mewarisi dari kelas Anda karena tidak semua konsumen Anda mungkin menggunakan TypeScript .

Hal yang sama dapat dikatakan untuk C# dan Java, tetapi saya tidak memerlukan pemeriksaan runtime karena saya memiliki sealed dan final untuk melakukannya untuk saya di tingkat kompiler.

Jika tidak semua konsumen saya menggunakan TypeScript, saya akan memiliki lebih banyak kekhawatiran daripada hanya mewarisi dari kelas final ; misalnya, akses ke anggota private atau protected , tidak ada pemeriksaan tipe statis, atau kemampuan untuk membuat kelas abstract .

Haruskah kode kita benar-benar dipenuhi dengan pemeriksaan runtime untuk semua hal ini (beberapa di antaranya bahkan tidak mungkin saya tambahkan) hanya untuk memastikan konsumen non-TypeScript kita seaman mungkin?

Anda dapat menulis aturan lint untuk menerapkan konvensi gaya yang tidak diwarisi dari kelas tertentu

Ini menyiratkan bahwa siapa pun yang menggunakan kode Anda menjalankan linter, jadi itu masih bukan solusi yang tepat; itu solusi.

Skenario bahwa seseorang akan "secara tidak sengaja" mewarisi dari kelas Anda yang harus disegel agak aneh. Saya juga berpendapat bahwa penilaian individu seseorang tentang apakah "mungkin" untuk mewarisi dari kelas tertentu mungkin agak mencurigakan.

Pengembang Bagus/Hebat memikirkan alasan mereka melakukan sesuatu, seperti memperluas kelas. Kurang dari itu dan Anda berakhir dengan mentalitas "tetapi berhasil", tanpa benar-benar memahami alasannya.

Mengajukan pertanyaan yang dimulai dengan bisakah saya biasanya cenderung memiliki jawaban yang lebih pasti daripada pertanyaan yang dimulai dengan haruskah saya .

Pada dasarnya ada tiga alasan untuk menyegel kelas: Keamanan, kinerja, dan niat. Dua di antaranya tidak masuk akal dalam TypeScript, jadi kita harus mempertimbangkan kompleksitas vs keuntungan dari sesuatu yang hanya melakukan 1/3 pekerjaan yang dilakukannya di runtime lain.

Boleh dibilang saya akan mengatakan bahwa TypeScript benar-benar akan memenuhi dua alasan ini: Keamanan dan niat, namun jika ini diimplementasikan murni sebagai pemeriksaan waktu kompilasi, maka itu hanya bisa melakukan ini di tingkat kompiler, daripada, seperti yang Anda tunjukkan dengan benar , pada tingkat waktu proses.

Saya tidak berpikir bahwa tidak memiliki final membahayakan kemampuan saya untuk menggunakan kelas JavaScript secara efektif, juga kelas JavaScript tidak dapat digunakan tanpanya. Saya merasa bahwa final lebih pada sisi fitur "baik untuk dimiliki", daripada fitur "perlu", tetapi kemudian hal yang sama dapat dikatakan untuk banyak fitur TypeScript.

Saya pikir keluhan utama di sini adalah bahwa TypeScript memungkinkan kita untuk membuat kelas abstract dan _open_, tetapi dalam hal pewarisan dan polimorfisme itu tidak memungkinkan kita untuk melukis gambar lengkap dengan cara yang elegan dan ekspresif.

Menyembunyikan konstruktor bukanlah hal yang diinginkan komunitas karena ini mengambil nilai semantik dan ekspresif dari implementasi kelas, dan sekali lagi, karena pengubah akses adalah "bagus untuk dimiliki", ini bahkan tidak dapat diterapkan saat runtime; pada dasarnya situasi "tidak ada dalam JavaScript".

Sebagai pengembang saya punya banyak topi; saat ini saya memiliki topi C#, topi Kotlin, dan topi TypeScript.

  • Dengan topi Kotlin saya, saya tidak perlu khawatir tentang final karena kelas sudah final secara default.
  • Dengan topi C# saya, saya menerapkan sealed sebagai kebiasaan, memaksa konsumen saya untuk bertanya bisakah saya daripada bertanya haruskah saya . Lebih sering daripada tidak, kelas C# sebenarnya adalah sealed dan sering diabaikan.
  • Dengan topi TypeScript saya, saya harus menyembunyikan konstruktor sebagai solusi, karena menurut pendapat saya (dan komunitas), bahasa tersebut kekurangan sesuatu yang berguna.

Karena tertarik, ketika tim TypeScript duduk mengelilingi meja dan mendiskusikan kelas abstract , apakah ada yang mengangkat tangan dan berkata "tapi bagaimana dengan final "?

@RyanCavanaugh Hum Saya mengerti poin Anda

Saya pikir sebagian besar itu adalah kesalahpahaman (saya kira dari bagaimana kami menjelaskan)

@ series0ne ringkasan dengan sangat baik bahwa niat kami tidak terlalu memperumit output TypeScript dengan meminta untuk membuat kelas akhir di JS (heck ini akan menjadi kecelakaan kereta untuk melakukannya) tetapi hanya membuat aturan tambahan untuk memberi tahu orang-orang: Tidak, Anda bisa' t lakukan itu. dan itu memberi tahu pengguna API bahwa mereka mungkin tidak boleh mengacaukan kelas itu (untuk stabilitas, alasan keamanan, dll) SEMUA pada file TypeScript/kompiler BUKAN pada output.

Apakah saya pikir akan lebih baik untuk memiliki fitur ini : ya itu bagus untuk memiliki cara yang lebih cepat untuk melakukan beberapa teknik.

Apakah itu SANGAT dibutuhkan? Tidak benar-benar ini bisa menjadi sesuatu yang kecil dan tidak boleh menjadi prioritas jika tim TypeScript mampu mengimplementasikan ekstra ini maka itu bagus untuk dimiliki.

Saya memang melihat bahwa kata kunci final/tersegel dapat memperumit BANYAK beberapa aspek terutama: Perbaikan bug, Patch, perubahan perilaku.

jika saya menjelaskan ini, saya benar-benar dapat melihat penggunaan final yang kasar dengan mengunci SEMUA kelas agar tidak diperpanjang. Memprovokasi IMO sangat sulit untuk ditangani. Saya tidak ingin menggunakan API tempat saya dikunci untuk melakukan perubahan apa pun pada kelas pengguna dengan pewarisan karena mereka paranoid karena "keamanan".

Ini berarti saya harus menggunakan prototipe lalu alias atau menimpa kelas ini hanya demi dapat mengubah perilaku kelas ini agar sesuai dengan kebutuhan saya.
ini akan mengganggu terutama jika saya hanya ingin menambahkan fungsionalitas tambahan ke kelas tersebut dan tidak ingin menggunakan alur kerja yang merusak.

Setelah ini hanya saya, saya mungkin salah paham dengan situasi saat ini, jadi bebaslah untuk menjelaskannya kepada saya :)

Ah jika saya ingat saya membaca banyak tentang ES4 (saya tidak yakin saya masih anak-anak ketika draft itu dalam produksi lol) dan mengejutkan itu terlihat sangat mirip dengan TypeScript.

dan dikatakan dalam spesifikasi draf di sini:
https://www.ecma-international.org/activities/Languages/Language%20overview.pdf

A class that does not explicitly extend any other class is said to extend the class Object. As a consequence, all the classes that exist form an inheritance hierarchy that is a tree, the root of which is Object. A class designated as final cannot be extended, and final methods cannot be overridden: class C { final function f(n) { return n*2 } } final class D extends C { function g() { return 37 } }

jadi Secara teknis kata kunci terakhir direncanakan ke dalam Ecmascript edisi ke-4. Apakah kita akan melihatnya di masa depan integrasi JS Saya ragu tentang itu tetapi mereka memang kelas terintegrasi jadi ini mungkin tetapi siapa yang tahu?

Saya memiliki satu kasus penggunaan yang baru saja saya temui yang mendorong saya untuk menemukan masalah ini. Ini berkisar pada penggunaan this sebagai tipe pengembalian:

abstract class TileEntity {
    //constructor, other methods...
    abstract cloneAt(x: number, y: number): this;
}

Niat saya menulis ini adalah untuk memastikan bahwa TileEntities tidak dapat diubah (dan karenanya aman untuk diedarkan) tetapi TileEntity yang diberikan dapat dikloning dengan beberapa properti yang diubah (dalam hal ini x dan y ) tanpa harus tahu apa-apa tentang bentuk subclass. Itulah yang ingin saya tulis, tetapi mencoba menulis implementasi (dengan benar) menyebabkan ini rusak.

class TurretTileEntity extends TileEntity {
    //constructor, other methods...
    cloneAt(x: number, y: number): this {
        return new TurretTileEntity(x, y, /* ... other properties */);
    }
}

Masalahnya adalah bahwa tanpa dapat mendeklarasikan bahwa _never_ akan ada subkelas TurretTileEntity, tipe pengembalian this tidak dapat dipersempit menjadi hanya tipe TurretTileEntity.

Dampak dari kekurangan ini di TypeScript adalah minimal, mengingat Anda dapat memberikan nilai kembalian ke <any> . Ini, secara objektif, merupakan antipola, dan indikasi yang berguna bahwa sistem tipe bisa lebih ekspresif. Kelas akhir/tersegel harus didukung.

Karena tertarik, ketika tim TypeScript duduk mengelilingi meja dan mendiskusikan kelas abstrak, apakah ada yang mengangkat tangan dan berkata "tapi bagaimana dengan final"?

Ya. Setiap kali kita menambahkan kata kunci, "tetapi kemudian kita harus menambahkan kata kunci lain ini juga!" adalah poin yang kuat untuk tidak menambahkannya, karena kami ingin menumbuhkan bahasa secermat mungkin. final pada deklarasi kelas ada di kamp ini juga - jika kita memiliki kelas final , maka kita akan "membutuhkan" metode atau properti final juga. Apa pun yang terlihat seperti scope creep dianggap dengan kecurigaan yang ekstrem.

Saya pikir kelas dan metode akhir sangat penting. Saya harap Anda akan menerapkannya.

Perhatikan bahwa masalah ini adalah tentang kelas akhir/tersegel. Ada diskusi di luar topik tentang metode akhir/tersegel yang merupakan konsep yang sama sekali berbeda.

mhegazy menutup https://github.com/Microsoft/TypeScript/issues/9264 untuk mendukung masalah ini. Apakah ada masalah terpisah yang tidak ditutup sebagai penipuan untuk melacak metode akhir/tersegel?

@crcla Dari membaca beberapa komentar @RyanCavanaugh di atas, saya percaya alasan untuk menutup masalah lainnya adalah karena mendukung kelas final/tersegel mencakup mendukung metode final/tersegel. Keduanya merupakan topik yang cukup erat hubungannya.

Saya tidak percaya proposal untuk final diberi label sebagai Won't fix .

Ubah label kembali ke In Discussion dan terapkan!

Dalam kasus saya, saya ingin menulis kelas dasar abstrak untuk plugin dan saya ingin memastikan bahwa kelas plugin tidak dapat menimpa metode kelas dasar saya secara tidak sengaja. Tak satu pun dari private constructor , Object.seal dapat melakukan pekerjaan ini.

Bagi mereka yang melihat ini sebagai fitur penting, hidup atau mati: sebenarnya tidak. Jika API Anda terdiri dari mengekspos implementasi kelas yang perlu diperluas oleh konsumen, ada cara yang lebih baik untuk melakukannya. Jangan ikuti contoh buruk React. Cukup gunakan fungsi dan objek sederhana, dan tinggalkan omong kosong OO ini di tempatnya: di masa lalu.

Sekarang berikan downvotes

Bisakah seseorang menjelaskan kepada saya bagaimana diskusi ini tidak membedakan antara kelas akhir dan metode akhir? Tentu saja mencegah beberapa metode dari override menambah nilai yang sangat besar. Saya ingin memberikan kemungkinan bagi pengembang plugin untuk menambahkan fungsionalitas dengan mensubklasifikasikan beberapa kelas kerangka kerja saya dan mengimplementasikan fungsi abstrak - tetapi pada saat yang sama memastikan dia tidak (mengapa!) menimpa metode yang memanggil fungsi abstrak ini (dan pastikan penanganan/loggin/pemeriksaan kesalahan terjadi.

@pelotom Jika Anda tidak ingin menggunakan konstruksi dan pola OOP, jangan, tidak ada yang menyuruh Anda untuk melakukan sebaliknya, namun hal yang sama harus diterapkan ke arah yang berlawanan: jika seseorang ingin menggunakan konstruksi OOP, biarkan saja.
Dengan memaksa orang lain untuk mengadopsi pola tertentu (non-OOP), Anda mengadopsi perilaku yang sama dengan yang Anda tolak OOP. Setidaknya konsisten :)

Menambahkan kata kunci final ke TypeScript bukanlah perubahan yang merusak dan tidak akan memengaruhi proyek Anda yang ada dengan cara apa pun, namun itu akan menjadi fitur penting bagi orang-orang seperti @thorek , saya sendiri, dan banyak lainnya.

jika seseorang ingin menggunakan konstruksi OOP, biarkan saja.

Kami tidak memperdebatkan apakah orang harus menggunakan konstruksi OOP, kami memperdebatkan apakah konstruksi OOP yang saat ini tidak ada harus ditambahkan ke bahasa. Melakukannya membutuhkan waktu pengembang yang dapat dihabiskan untuk fitur lain, dan membuat bahasa lebih kompleks, yang memperlambat laju fitur masa depan (karena untuk setiap fitur baru harus dipertimbangkan bagaimana ia akan berinteraksi dengan setiap fitur lama). Jadi itu pasti mempengaruhi saya, karena saya lebih suka menghabiskan anggaran pengembangan dan kompleksitas TypeScript untuk hal-hal yang lebih berguna!

fwiw, kami mungkin akan mendorong pelanggan kami (google) untuk menggunakan @final dalam komentar sehingga menyebar ke kompiler penutupan :\

Menambahkan _final_ pada kelas dan metode berguna saat tim R&D membuat pustaka TS yang dapat digunakan oleh tim lain. Kata kunci ini (penting) bagian dari stabilitas eksposur layanan alat tersebut.
Kata kunci lebih penting untuk tim besar dan kurang penting untuk proyek kecil, imho.

@dimiVergos Ini persis kasus saya, dan saya menganggap hal yang sama untuk banyak orang lain. Saya dapat membayangkan bahwa saya mungkin tampak bias karena posisi saya, tetapi tampaknya satu-satunya (walaupun sangat) penggunaan kata kunci ini yang dapat dibenarkan menurut pengalaman saya.

Saya terbuka untuk koreksi yang satu ini!

Saya lebih peduli tentang mendukung metode akhir (untuk menghindari subkelas yang secara tidak sengaja menimpanya) daripada yang saya lakukan pada kelas akhir. Apakah tiket yang tepat untuk memilih itu? Tiket https://github.com/Microsoft/TypeScript/issues/9264 tampaknya menyiratkan demikian.

Jadi bagaimana saya memilih "metode akhir"? Atau apakah saya hanya memilihnya dengan komentar ini? Juga bagaimana kita menambahkan "metode" ke judul? Bisakah saya mengeditnya saja? Judul saat ini secara menyesatkan terbatas hanya pada kelas tetapi diskusi juga mencakup metode.

Seperti banyak orang lain, saya ingin melihat metode terakhir di TypeScript. Saya telah menggunakan pendekatan berikut sebagai solusi parsial yang tidak kebal terhadap kegagalan tetapi setidaknya membantu sedikit:

class parent {
  public get finalMethod(): () => void {
    return () => {
      // final logic
    };
  }
}

class child extends parent {
  // ERROR "Class 'parent' defines instance member accessor 'finalMethod', but extended class 'child' defines it as instance member function"
  public finalMethod(): void { }
}

Inilah kasus penggunaan dunia nyata lainnya untuk metode final . Pertimbangkan ekstensi berikut dari React.Component untuk mempermudah penerapan Context sederhana.

interface FooStore{
  foo: string;
}

const {Provider, Consumer} = React.createContext<FooStore>({
  foo: 'bar';
});

abstract class FooConsumerComponent<P, S> extends React.Component<P, S> {

  // implement this instead of render
  public abstract renderWithStore(store: FooStore): JSX.Element;

  public final render() {
    return (
      <Consumer>
        {
          (store) => {
            return this.renderWithStore(store);
          }
        }
      </Consumer>
    );
  }
}

Tanpa final pada metode render() , tidak ada yang menghentikan seseorang dari kesalahan menimpa metode, sehingga merusak segalanya.

Hai, seperti banyak lainnya, saya ingin kata kunci "final" atau "disegel" setidaknya untuk metode. Saya setuju sepenuhnya bahwa kata kunci ini adalah nilai tambah yang sangat besar karena memungkinkan pengembang untuk mencegah pelanggan perpustakaan mereka memperluas blok kode penting (seperti di plugin misalnya).

Satu-satunya solusi yang saya temukan adalah menggunakan dekorator metode seperti:
function sealed(target, key,descriptor){ descriptor.writable = false; // Prevent method to be overriden }

kemudian di kelas "dasar" Anda, gunakan dekorator untuk mencegah penggantian metode, contoh:

class MyBaseClass {
<strong i="10">@sealed</strong>
public MySealedMethod(){}

public OtherMethod(){}

...
}

Dengan cara ini 'MySealedMethod' tidak dapat (dengan mudah) ditimpa di sub kelas. Contoh:

class AnotherClass extends MyBaseClass {
public MySealedMethod(){} ====>> ERROR at RUNTIME (not at compile time)
}

sebenarnya, satu-satunya downside dari solusi ini adalah bahwa "disegel" hanya terlihat saat runtime (kesalahan di konsol javascript) tetapi BUKAN pada waktu kompilasi (karena properti metode diatur melalui fungsi, saya kira)

@RyanCavanaugh

Ya. Setiap kali kita menambahkan kata kunci, "tetapi kemudian kita harus menambahkan kata kunci lain ini juga!" adalah poin yang kuat untuk tidak menambahkannya, karena kami ingin menumbuhkan bahasa secermat mungkin.

Tampaknya bagi saya bahwa Anda memulai argumen ini dengan pikiran yang menentang fitur tersebut. Fitur populer tidak boleh menjadi alasan untuk tidak mempertimbangkan fitur tersebut. Ini harus menjadi titik untuk bertanya pada diri sendiri _mengapa itu populer_, dan kemudian mempertimbangkannya dengan cermat.

Saya yakin Anda telah mendiskusikannya dan argumen telah diajukan untuk mempertimbangkannya atau menentangnya. Apakah argumen yang menentangnya hanya karena fitur creep, atau adakah yang lebih spesifik? Apakah Anda menganggapnya sebagai fitur pinggiran? Jika demikian, dapatkah kami melakukan sesuatu untuk mengubah pikiran Anda?

Biarkan saya meninggalkan pertanyaan-pertanyaan ini di sini untuk kita semua renungkan.

Apakah ada cara untuk menyegel kelas _by design_ menggunakan konstruksi saat ini?
Jika fitur itu ditambahkan, apakah itu memungkinkan kita untuk melakukan sesuatu yang baru? (Seperti dalam, sesuatu yang ada konstruksi tidak mengizinkan)
Jika fitur itu memungkinkan kita melakukan sesuatu yang baru, apakah itu sesuatu yang berharga?
Apakah menambahkan kelas yang disegel akan meningkatkan kompleksitas bahasa terlalu banyak?
Apakah menambahkan kelas yang disegel akan meningkatkan kompleksitas kompiler terlalu banyak?

Untuk tujuan membahas ini, saya tidak menyertakan metode tersegel dalam diskusi dan saya juga tidak menyertakan pemeriksaan run-time dalam implementasinya. Kita semua mengerti bahwa kita tidak dapat menerapkan ini pada Javascript.

FYI: Saya akan luar biasa jika counter untuk fitur ini sedikit lebih dari sekedar pernyataan menyeluruh terhadap fitur-creep.

@asimonf Saya pikir semua yang Anda katakan sudah ditangani oleh komentar ini (Anda perlu memperluas bagian "tampilkan lebih banyak" untuk melihatnya) dan yang mengikutinya.

Apa yang akan mengubah pikiran kita di sini? Orang-orang muncul dengan contoh kode yang tidak berfungsi sebagaimana mestinya karena kata kunci sealed tidak ada .

Skenario di sini yang coba ditangani oleh kata kunci adalah skenario di mana seseorang secara keliru mewarisi dari suatu kelas. Bagaimana Anda secara keliru mewarisi dari suatu kelas? Bukan kesalahan yang mudah untuk dilakukan. Kelas JavaScript secara inheren cukup rapuh sehingga setiap subkelas harus memahami persyaratan perilaku kelas dasarnya, yang berarti menulis beberapa bentuk dokumentasi, dan baris pertama dari dokumentasi itu dapat berupa "Jangan subkelas kelas ini". Atau mungkin ada pemeriksaan runtime. Atau konstruktor kelas dapat disembunyikan di balik metode pabrik. Atau sejumlah opsi lainnya.

Mengapa TypeScript harus memiliki kata kunci ini ketika situasinya sudah menjadi satu di mana Anda seharusnya mengalami setidaknya dua penghalang jalan terpisah yang memberi tahu Anda untuk tidak berada di sana sejak awal?

Saya telah memprogram selama kira-kira selama bertahun-tahun sekarang dan saya belum pernah mencoba mensubkelaskan sesuatu hanya untuk mengetahui itu disegel dan saya seharusnya tidak mencoba. Itu bukan karena aku super jenius! Itu hanya kesalahan yang sangat tidak biasa untuk dilakukan, dan dalam JavaScript situasi ini sudah menjadi situasi di mana Anda harus mengajukan pertanyaan tentang kelas dasar di tempat pertama. Jadi masalah apa yang sedang diselesaikan ?

Kasus nyata yang akan saya gunakan final adalah untuk mencegah menimpa metode dengan sesuatu yang tidak akan pernah memanggil metode super. Beberapa metode publik tentu saja. Tetapi solusi sebenarnya yang saya temukan adalah kemampuan untuk memaksa sub metode apa pun untuk memanggil yang super. https://github.com/Microsoft/TypeScript/issues/21388

Karena saya bukan perancang bahasa, saya tidak dapat berbicara tentang kekurangan menambahkan kata kunci ke bahasa tersebut. Saya akan mengatakan bahwa saat ini saya sedang mengerjakan superclass abstrak yang memiliki metode yang ingin saya selesaikan, tetapi saya kecewa karena saya tidak dapat melakukannya. Untuk saat ini saya akan menggunakan pendekatan yang kurang ideal untuk memberikan komentar, tetapi anggota tim yang mengimplementasikan kelas mungkin tidak membaca komentar tersebut.

Saya pikir di atas ada banyak contoh bagus tentang kapan/di mana final tidak hanya berguna, tetapi juga memberikan _nilai nyata_ dan _keamanan nyata_ ke platform yang sedang dibangun. Tidak ada bedanya bagi saya. Seperti yang saya katakan, saya tidak tahu banyak tentang kompleksitas desain bahasa tetapi ini jelas merupakan fitur yang diinginkan/akan digunakan orang/saya tidak dapat benar-benar melihat cara untuk menyalahgunakannya atau itu akan menghasilkan kode yang buruk. Saya memahami konsep ketakutan lingkup merayap tetapi pengembang TS tidak harus mengimplementasikan semua yang diinginkan komunitas.

Hanya untuk menambahkan sedikit perdebatan saya, saya ingin meletakkan jempol saya pada skala ya karena masalah ini penuh dengan argumen bagus yang mendukung penambahan fitur, namun argumen menentang yang saya lihat adalah:

  • Ini dapat menyebabkan scope creep (ini dapat dikelola)
  • Orang akan meminta fitur lain (oke, tapi siapa peduli, itu bagian dari mendesain bahasa dan ini bukan preseden hukum yang kami tetapkan)
  • Itu dapat mengurangi efisiensi (saya membayangkan bahwa karena tim TS penuh dengan orang jahat, Anda mungkin akan menemukan cara untuk membuat ini dapat dikelola)
  • Berkomentar "Jangan gunakan konsep OOP"

Tak satu pun di atas tampak seperti ancaman yang kredibel terhadap ekosistem TS itu sendiri, atau kode yang dihasilkan darinya; mereka semua tampak politis (kecuali mungkin efisiensi, tapi saya memiliki kepercayaan besar pada kalian jadi saya tidak terlalu khawatir tentang hal itu, ditambah penurunan efisiensi kecil akan bernilai tidak meledakkan komponen penting/konstruksi perpustakaan dengan kesalahan warisan tanpa disadari) .

Semoga ini konstruktif! Suka TS dan ingin melihatnya terus meningkat!

👍

--edit--

Menanggapi @RyanCavanaugh :

So what problem is being solved?

Masalah yang sedang dipecahkan saya pikir diuraikan oleh banyak, banyak komentar di atas. Saya tidak bermaksud ini sebagai penghinaan tetapi pertanyaan itu memang terdengar, bagi saya, seperti Anda tidak benar-benar memperhatikan argumen yang disajikan, dan bahwa Anda memiliki pikiran untuk "tidak". Saya tidak bermaksud tidak hormat dengan itu, hanya saja, sejujurnya, utas ini penuh dengan contoh masalah yang akan diselesaikan.

Entah bagaimana saya melewatkan komentar ini , yang dengan sangat baik menguraikan perhatian serius untuk menambahkan fitur dan itu masuk akal bagi saya. Sekali lagi, saya tidak tahu kerumitan menambahkannya tetapi mengesampingkan masalah efisiensi (karena pengembang tidak perlu khawatir tentang efisiensi TS, itulah tugas tim TS), sepertinya tidak ada argumen bagus yang menentang final (dalam pendapat saya yang paling sederhana).

Sebagian besar diskusi baru-baru ini dipusatkan pada metode akhir (lebih sedikit pada kelas akhir). Metode terakhir tentu saja merupakan minat utama saya. Saya baru menyadari bahwa jika Anda memperluas tautan "komentar ini" di pos sebelumnya @RyanCavanaugh , ia menganggap metode diskusi terakhir di luar topik. Namun @mhegazy menyarankan bahwa tiket ini adalah tempat yang tepat (saat menutup #9264). Jadi sebagai pengguna akhir yang rendah saya bingung ke mana saya harus meminta/memilih untuk metode akhir. Bimbingan akan dihargai.

@cbutterfield
Kontradiksi adalah sesuatu yang saya perhatikan juga tetapi tidak menunjukkan dalam posting saya karena takut mengomel (saya benar-benar menemukan utas INI melalui # 9264 dan datang ke sini atas arahan @mhegazy ).
@RyanCavanaugh
@mhegazy
Per komentar di atas, di mana _is_ tempat yang tepat untuk membahas metode terakhir, karena itu sebenarnya adalah hal yang paling saya minati.

Melihat kembali komentar saya yang class pada penggunanya! Dengan proposal Hooks, tim React telah sadar dan menggunakan pendekatan fungsional yang lebih sederhana secara radikal. Dengan itu, alasan terakhir bagi banyak pengembang untuk harus menggunakan kelas sama sekali telah menguap, dan selamat tinggal. Saya akan mengulangi: semua yang Anda pikir Anda butuhkan untuk kelas dapat dilakukan lebih baik tanpa mereka .

Masa depan JS dan TS tanpa kelas, pujilah!

@cbutterfield Saya perhatikan itu juga, dan jika masalah #9264 ditutup untuk dibahas dalam masalah ini, saya pikir judulnya harus diubah menjadi Suggestion: Final keyword for classes and methods , jika tidak, orang yang mencari metode terakhir (secara eksklusif) mungkin tidak menambahkan reaksi dalam posting pertama.

Mengutip @mhegazy :
Java dan/atau C# menggunakan kelas final untuk mengoptimalkan kelas Anda saat runtime, mengetahui bahwa itu tidak akan menjadi khusus. ini menurut saya adalah nilai utama untuk dukungan akhir. Di TypeScript tidak ada yang bisa kami tawarkan untuk membuat kode Anda berjalan lebih baik daripada tanpa final.

Saya pada dasarnya tidak setuju. Saya telah mengembangkan di Java selama bertahun-tahun di banyak tim dan kami tidak pernah menggunakan kelas atau metode final untuk optimasi runtime; dalam praktiknya ini terutama merupakan idiom desain OOP. Kadang-kadang saya hanya tidak ingin kelas atau metode saya diperluas/ditimpa oleh subkelas eksternal baik itu untuk melindungi dan menjamin suatu fungsi, atau untuk membatasi kebisingan API perpustakaan ke hal-hal penting, atau untuk melindungi pengguna API dari kesalahpahaman tentang API yang kompleks.

Ya, masalah tersebut dapat diselesaikan dengan menambahkan komentar atau antarmuka, tetapi final adalah solusi elegan untuk mengurangi API yang terlihat ke fungsi yang diperlukan ketika semua yang Anda inginkan adalah kelas sederhana dengan API bersih yang dapat diperpanjang.

Meskipun masalah ini hampir berusia 3 tahun, kita benar-benar harus memiliki kata kunci final karena sudah ada kata kunci abstract .

Perlu diingat bahwa kami tidak ingin memaksa orang untuk menggunakannya dan ini adalah fitur yang berguna seperti kata kunci abstrak. Tapi inilah kasus penggunaan lain yang akan sangat menunjukkan manfaat dari kata kunci final :

abstract class A {
  protected abstract myVar: string;

  protected abstract myFunction(): void;
}

class B extends A {
  protected readonly myVar: string = "toto";

  constructor() {
    super();
    this.myFunction();
  }

  protected myFunction() {
    console.log(this.myVar);
  }
}

class C extends B {
  constructor() {
    super();
  }

  protected myFunction() {
    console.log("tata");
  };

  public callFunction = () => {
    this.myFunction();
  }
}

const myB = new B(); // toto

const myC = new C(); // toto
myC.callFunction(); // tata

Hasil setelah kompilasi:

toto
tata

Jadi dalam kode ini, Kami memiliki kelas abstrak A yang memiliki metode dan properti abstrak, kami ingin kelas yang diwarisi untuk mendefinisikannya, tetapi kami ingin tidak membiarkan kelas lain menimpa implementasi tersebut.
Yang terbaik yang dapat kami lakukan adalah mempertahankan kata kunci protected , tetapi seperti yang Anda lihat, kami masih dapat mendefinisikan ulang atributnya.

Jadi bagaimana jika kita melangkah lebih jauh dalam proses kompilasi TypeScript dan menggunakan readonly untuk melindungi atribut kita (dan berpura-pura metode kita adalah properti) ?

class B extends A {
  [...]

  protected readonly myFunction = () => {
    console.log(this.myVar);
  }
}

class C extends B {
  protected myVar: string = "I am not protected that much";
  [...]

  protected myFunction = () => { // using the readonly keyword doesn't prevent any modification
    console.log("tata"); // If you launch this code, tata will still be logged in the console.
  };
}

(Perhatikan bahwa kode dikompilasi dengan tsc, tidak ada kesalahan yang muncul saat mengompilasi atau menjalankannya)
Jadi sekarang kita punya dua masalah:

  • Kami tidak melindungi atribut B , jadi kami membutuhkan cara untuk mencegah pewarisan tak terduga.
  • Sekarang kita tahu bahwa kita dapat mengganti properti readonly dengan memperluas kelasnya, dan YA, TypeScript tahu bahwa itu adalah properti induk yang kita ubah karena menggunakan private alih-alih protected di kelas C akan menimbulkan kesalahan karena bukan jenis/visibilitas akses yang sama.

Saat ini, kita dapat menggunakan dekorator ( disegel seperti yang ditunjukkan dokumentasi TypeScript resmi, atau dekorator akhir ) tetapi ingat bahwa ini hanya berguna saat runtime, dan sebagai gantinya kita harus mencegahnya dalam proses kompilasi.

Saya akan melihat apakah ada masalah tentang "pelanggaran keamanan" (jika kita dapat menyebutnya demikian) ketika kita ingin menimpa nilai readonly yang dilindungi dan kita benar-benar bisa dan tidak seharusnya. Kalau tidak, saya akan membuka masalah untuk memperdebatkan verifikasi kata kunci readonly antara private , protected kata kunci dengan barang warisan.

tl; dr : Kata kunci final akan menyelesaikan dua masalah (meskipun verifikasi readonly akan menjadi awal yang baik untuk mencegah penulisan ulang yang tidak sah)

Tiga tahun kemudian... itu sangat konyol.

Untuk membuktikan perlunya fitur ini, saya sarankan untuk melihat apa yang dilakukan dalam bahasa lain:

Java:
final class SomeClass { ... }
PHP:
final class SomeClass { ... }
C#:
sealed class SomeClass {...}
C++:
class SomeClass final { ... }
Delphi:
type SomeClass = class sealed() ... end;

Jadi, kami melihat bahwa di semua bahasa OOP paling populer, fitur ini ada karena berasal dari logika pewarisan OOP.

Saya juga harus mengutip kata-kata Dev lead TS

jika kita memiliki kelas akhir, maka kita juga akan "membutuhkan" metode atau properti akhir.

Saya tidak yakin apakah itu ironis, tetapi di JS kata kunci readonly digunakan untuk properti (dan metode jika Anda curang), jadi itu poin yang cukup bodoh.

Dan mungkin tidak membiarkan satu orang menutup masalah ketika dia memiliki +40 downvotes yang berarti komunitas sangat tidak setuju dengannya akan menjadi saran yang baik untuk menghindari situasi bodoh lebih lanjut.

Jika Anda tidak tertarik untuk memperkuat fitur utama TypeScript, harap ucapkan dengan lantang sehingga berita teknologi dapat menyampaikannya di Twitter, karena jika tidak, itu hanya rumor buruk yang tersembunyi. Anda hanya mengabaikan komunitas Anda, DAN Anda tidak memiliki proses dalam membuat komunitas memvalidasi apa yang kami butuhkan atau tidak.

mungkin tidak membiarkan satu orang menutup masalah ketika dia memiliki +40 downvotes yang berarti komunitas sangat tidak setuju dengannya akan menjadi saran yang baik untuk menghindari situasi bodoh lebih lanjut

Saya bukan "satu orang". Ketika saya membuat keputusan di sini, itu adalah cerminan dari proses pengambilan keputusan seluruh tim TypeScript. Tim desain telah melihat masalah ini beberapa kali, dan setiap kali sampai pada kesimpulan yang sama. Anda dipersilakan untuk tidak setuju dengan hasil itu, tetapi prosesnya tidak untuk diperdebatkan.

Anda tidak memiliki proses dalam membuat komunitas memvalidasi apa yang kita butuhkan atau tidak.

Ini adalah proses, di sini: Anda memanggil summarization saya penalaran kita "bodoh" dan saya (dan orang lain di tim) membacanya. Kami mendengarkan umpan balik.

Saya tahu menunjuk ke fitur lain yang diimplementasikan sebagai alasan untuk mempertimbangkan yang ini adalah bau dan bukan argumen, tetapi saya merasa ironis bahwa mekanisme aliasing tipe dipertimbangkan dan ditambahkan (mungkin luar biasa, tetapi orang pasti bisa hidup tanpanya ) tetapi sesuatu seperti ini ditolak.

Pada akhirnya, saya bisa hidup tanpa fitur seperti itu, tetapi hal yang sama dapat dikatakan untuk sekitar setengah fitur yang dimiliki TS. Jadi, apa alasan yang tepat untuk mempertimbangkan penerapan ini?

GitHub menyembunyikan banyak komentar di sini, jadi saya memposting ulang berbagai tanggapan (ditambah beberapa tambahan) yang telah kami berikan sebelumnya. Menambahkan beberapa pemikiran di bawah juga.


Beberapa poin dipertimbangkan ketika kami meninjau ini terakhir kali

  • KELAS ADALAH FITUR JAVASCRIPT, BUKAN FITUR TYPESCRIPT . Jika Anda benar-benar merasa bahwa kelas sama sekali tidak berguna tanpa final , itu adalah sesuatu untuk dibicarakan dengan komite TC39 atau dibawa ke es-discuss. Jika tidak ada yang mau atau mampu meyakinkan TC39 bahwa final adalah fitur yang harus dimiliki, maka kita dapat mempertimbangkan untuk menambahkannya ke TypeScript.
  • Jika legal untuk memanggil new pada sesuatu, itu memiliki korespondensi runtime satu-ke-satu dengan mewarisi darinya. Gagasan tentang sesuatu yang bisa new 'd tetapi tidak super 'd tidak ada dalam JavaScript
  • Kami mencoba yang terbaik untuk menghindari mengubah TypeScript menjadi sup kata kunci OOP. Ada banyak mekanisme komposisi yang lebih baik daripada pewarisan dalam JavaScript dan kita tidak perlu memiliki setiap kata kunci yang dimiliki C# dan Java.
  • Anda dapat dengan mudah menulis pemeriksaan runtime untuk mendeteksi pewarisan dari kelas "seharusnya menjadi final", yang tetap harus Anda lakukan jika Anda "khawatir" tentang seseorang yang secara tidak sengaja mewarisi dari kelas Anda karena tidak semua konsumen Anda mungkin menggunakan TypeScript .
  • Anda dapat menulis aturan lint untuk menerapkan konvensi gaya yang tidak diwarisi dari kelas tertentu
  • Skenario bahwa seseorang akan "secara tidak sengaja" mewarisi dari kelas Anda yang harus disegel agak aneh. Saya juga berpendapat bahwa penilaian individu seseorang tentang apakah "mungkin" untuk mewarisi dari kelas tertentu mungkin agak mencurigakan.
  • Pada dasarnya ada tiga alasan untuk menyegel kelas: Keamanan, kinerja, dan niat. Dua di antaranya tidak masuk akal dalam TypeScript, jadi kita harus mempertimbangkan kompleksitas vs keuntungan dari sesuatu yang hanya melakukan 1/3 pekerjaan yang dilakukannya di runtime lain.

belum mendengar argumen yang meyakinkan mengapa TS tidak membutuhkannya

Sekedar mengingatkan bahwa ini bukan cara kerjanya. Semua fitur mulai dari -100 . Dari postingan itu

Dalam beberapa pertanyaan tersebut, pertanyaan tersebut menanyakan mengapa kita “mengeluarkan” atau “meninggalkan” fitur tertentu. Kata-kata itu menyiratkan bahwa kami memulai dengan bahasa yang ada (C++ dan Java adalah pilihan populer di sini), dan kemudian mulai menghapus fitur hingga kami mencapai titik yang kami sukai. Dan, meskipun mungkin sulit bagi sebagian orang untuk percaya, bukan itu cara bahasa dirancang.

Kami memiliki anggaran kompleksitas yang terbatas. Semua yang kami lakukan membuat setiap hal berikutnya yang kami lakukan 0,1% lebih lambat. Saat Anda meminta fitur, Anda meminta agar setiap fitur lainnya menjadi sedikit lebih sulit, sedikit lebih lambat, sedikit lebih mungkin. Tidak ada yang masuk ke sini karena Java memilikinya atau karena C# memilikinya atau karena hanya beberapa baris kode atau Anda dapat menambahkan switch baris perintah . Fitur perlu membayar untuk diri mereka sendiri dan untuk seluruh beban mereka di masa depan.


Apa yang akan mengubah pikiran kita di sini? Orang-orang muncul dengan contoh kode yang tidak berfungsi sebagaimana mestinya karena kata kunci final tidak ada .

Skenario di sini yang coba ditangani oleh kata kunci adalah skenario di mana seseorang secara keliru mewarisi dari suatu kelas. Bagaimana Anda secara keliru mewarisi dari suatu kelas? Bukan kesalahan yang mudah untuk dilakukan. Kelas JavaScript secara inheren cukup rapuh sehingga setiap subkelas harus memahami persyaratan perilaku kelas dasarnya, yang berarti menulis beberapa bentuk dokumentasi, dan baris pertama dari dokumentasi itu dapat berupa "Jangan subkelas kelas ini". Atau mungkin ada pemeriksaan runtime. Atau konstruktor kelas dapat disembunyikan di balik metode pabrik. Atau sejumlah opsi lainnya.

Dengan kata lain: satu-satunya proses yang benar untuk mewarisi dari kelas dalam JavaScript selalu dimulai dengan membaca dokumentasi kelas itu . Ada terlalu banyak kendala yang mungkin harus diturunkan oleh kelas dasar dan memulai pengkodean. Jika, pada langkah pertama , Anda seharusnya sudah tahu untuk tidak mencoba, nilai apa yang diberikan penegakan statis pada langkah ketiga?

Mengapa TypeScript harus memiliki kata kunci ini ketika situasinya sudah menjadi satu di mana Anda seharusnya mengalami setidaknya dua penghalang jalan terpisah yang memberi tahu Anda untuk tidak berada di sana sejak awal?

Saya telah memprogram selama kira-kira selama bertahun-tahun sekarang dan saya belum pernah mencoba mensubkelaskan sesuatu hanya untuk mengetahui itu disegel dan saya seharusnya tidak mencoba. Itu bukan karena aku super jenius! Itu hanya kesalahan yang sangat tidak biasa untuk dilakukan, dan dalam JavaScript situasi ini sudah menjadi situasi di mana Anda harus mengajukan pertanyaan tentang kelas dasar di tempat pertama. Jadi masalah apa yang sedang diselesaikan ?

Jika Anda tidak tertarik untuk memperkuat fitur utama TypeScript, ucapkan dengan lantang agar berita teknologi dapat menyampaikannya di Twitter

Kami sangat eksplisit tentang bagaimana kami mendesain bahasa ; non-gol nomor satu berbicara persis dengan saran ini.


Reaksi pribadi saya setelah membaca banyak komentar di sini adalah bahwa ada efek bias yang kuat karena konstruksi yang ada dalam bahasa lain. Jika Anda hanya mendekati ini dari perspektif batu tulis kosong, ada lusinan perilaku yang dapat Anda bayangkan, di mana TypeScript hanya memiliki sebagian kecil.

Anda dapat dengan mudah membayangkan beberapa kata kunci yang diterapkan pada suatu metode dan menerapkan metode turunan yang memanggilnya melalui super . Jika C# dan Java memiliki kata kunci ini, orang akan benar - final , karena tidak mungkin untuk menerapkan pemeriksaan runtime, dan merupakan aspek yang jauh lebih halus dari kontrak turunan dasar daripada "kelas turunan mungkin tidak ada ". Ini akan berguna dalam berbagai cara yang final tidak akan berguna. Saya lebih suka memiliki kata kunci itu daripada yang ini (meskipun saya pikir tidak ada yang memenuhi kompleksitas vs bilah nilai).

Jadi, apa alasan yang tepat untuk mempertimbangkan penerapan ini?

Ketika kita melihat umpan balik, pertimbangan yang kuat terlihat seperti ini:

  • Saya tidak dapat secara memadai mengekspresikan perilaku perpustakaan JavaScript ini
  • Kode saya dikompilasi, tetapi seharusnya tidak karena memiliki kesalahan (halus) ini
  • Maksud dari kode ini tidak dikomunikasikan secara memadai oleh sistem tipe

final ini, tetapi juga pengubah yang mengatakan "Fungsi ini tidak dapat dipanggil dua kali berturut-turut" atau "Konstruksi kelas ini tidak benar-benar selesai sampai centang asinkron berikutnya". Tidak semua yang bisa dibayangkan harus ada.

Kami belum pernah melihat umpan balik seperti "Saya menghabiskan berjam-jam men-debug karena saya mencoba mewarisi dari kelas yang sebenarnya tidak dapat disubklasifikasikan" - seseorang mengatakan itu akan memicu 'wat' karena itu tidak benar-benar bisa dibayangkan. Bahkan jika pengubah ada, itu tidak akan benar - fs.FSwatcher final ? Sepertinya bahkan penulis simpul tidak tahu. Jadi final sudah cukup jika penulis tahu bahwa itu final , tetapi itu akan didokumentasikan terlepas dari itu , dan kurangnya final tidak benar-benar memberi tahu Anda apa pun karena sering sama sekali tidak diketahui.

Saya membaca seluruh blok dan memahami pernyataan tim tentang pilihan fitur, saya hanya akan kembali pada poin-poin tertentu dari komentar saya

Saya bukan "satu orang". Ketika saya membuat keputusan di sini, itu adalah cerminan dari proses pengambilan keputusan seluruh tim TypeScript.

Maaf saya mengacu pada mhegazy dan umpan balik yang cukup besar yang dia dapatkan dari komunitas yang menggunakan TypeScript.

Jika Anda benar-benar merasa kelas sama sekali tidak berguna tanpa final, itu adalah sesuatu untuk dibawa ke komite TC39 atau dibawa ke es-discuss. Jika tidak ada yang mau atau mampu meyakinkan TC39 bahwa final adalah fitur yang harus dimiliki, maka kita dapat mempertimbangkan untuk menambahkannya ke TypeScript.

Saya tidak berpikir final adalah langkah pertama karena sudah ada proposal untuk kata kunci private https://github.com/tc39/proposal-private-methods

Saya skeptis tentang You should a comment to say *dont do this* , ini seperti mengatakan pada formulir Hey don't write down specific characters , Anda membuat kode selama bertahun-tahun sehingga Anda harus mengetahui aturan n°1 dari dev: Never trust the user ( dan pengembang yang akan menggunakan kode Anda)

Saya tidak mengatakan kita harus benar-benar menghentikan semuanya dan menempatkan satu miliar dolar untuk mengimplementasikan kata kunci terakhir, karena usecases terlalu rendah untuk menjadi seefisien itu, juga pertimbangkan bahwa saya memberikan beberapa contoh di mana batasnya berasal dari TS dan JS dan itu jika orang memilih TS, itu untuk mencegah kesalahan dalam waktu kompilasi, bukan pada waktu berjalan. Jika kita ingin hal-hal meledak saat runtime, kita dapat menggunakan JS dan berhenti peduli tentang TS, tapi bukan itu intinya, karena ada kasus penggunaan utama yang menunjukkan bagaimana saya menggunakan kata kunci final : Saya ingin mengunci metode, saya tidak ingin ada yang menimpanya.

Dan karena Javascript dibatasi oleh itu, komunitas mengira TypeScript bisa melampaui batas itu, itulah mengapa masalah ini telah berlangsung selama 3 tahun, itu sebabnya orang masih bertanya-tanya mengapa fitur ini tidak ada di sini, dan itulah mengapa kami ingin kompilernya melakukan pemeriksaan manual kelas dan/atau metode.

Anda tidak menunggu JS memiliki metode privat/publik untuk mengimplementasikannya, meskipun sebenarnya ada proposal untuk memasukkannya dengan kata kunci # (lebih sedikit verbose dari public / private ), saya tahu Anda ingin membuat bahasa yang sempurna karena kesempurnaan bukanlah saat Anda tidak dapat menambahkan apa pun lagi, melainkan saat Anda tidak dapat menghapus apa pun lagi.

Jika Anda dapat menemukan solusi untuk mencegah metode ditimpa dalam proses kompilasi (dan tidak dalam proses runtime karena intinya tidak valid), jadilah tamu saya.

Saya mencoba menunjukkan kelemahan situasi (pada metode/properti dan bukan kelas), karena setiap pengembang TS dapat menulis ulang perpustakaan apa pun yang mereka inginkan, merusak apa pun yang mereka inginkan, mungkin itulah keindahannya, saya kira.

Terima kasih atas jawabannya, tidak ada kebencian atau perilaku buruk yang ditujukan terhadap tim pengembang atau Anda.

Semua poin valid! Tapi, tolong mari kita membagi diskusi antara

a) Mendukung kelas akhir
b) Mendukung metode akhir

Sementara semua argumen Anda menargetkan a) - tidak ada target b.

Memiliki metode akhir sangat penting seperti yang ditunjukkan berkali-kali dalam diskusi. Satu-satunya jawaban yang saya dengar sejauh ini adalah "jangan lakukan OOP" - tetapi saya (dan banyak lainnya) tidak akan setuju dengan itu.

Am 28.01.2019 um 20:32 schrieb Ryan Cavanaugh [email protected] :

GitHub menyembunyikan banyak komentar di sini, jadi saya memposting ulang berbagai tanggapan (ditambah beberapa tambahan) yang telah kami berikan sebelumnya. Menambahkan beberapa pemikiran di bawah juga.

Beberapa poin dipertimbangkan ketika kami meninjau ini terakhir kali

KELAS ADALAH FITUR JAVASCRIPT, BUKAN FITUR TYPESCRIPT. Jika Anda benar-benar merasa kelas sama sekali tidak berguna tanpa final, itu adalah sesuatu untuk dibawa ke komite TC39 atau dibawa ke es-discuss. Jika tidak ada yang mau atau mampu meyakinkan TC39 bahwa final adalah fitur yang harus dimiliki, maka kita dapat mempertimbangkan untuk menambahkannya ke TypeScript.
Jika legal untuk memanggil yang baru pada sesuatu, itu memiliki korespondensi runtime satu-ke-satu dengan mewarisi darinya. Gagasan tentang sesuatu yang bisa baru tetapi tidak super tidak ada di JavaScript
Kami mencoba yang terbaik untuk menghindari mengubah TypeScript menjadi sup kata kunci OOP. Ada banyak mekanisme komposisi yang lebih baik daripada pewarisan dalam JavaScript dan kita tidak perlu memiliki setiap kata kunci yang dimiliki C# dan Java.
Anda dapat dengan mudah menulis pemeriksaan runtime untuk mendeteksi pewarisan dari kelas "seharusnya menjadi final", yang tetap harus Anda lakukan jika Anda "khawatir" tentang seseorang yang secara tidak sengaja mewarisi dari kelas Anda karena tidak semua konsumen Anda mungkin menggunakan TypeScript .
Anda dapat menulis aturan lint untuk menerapkan konvensi gaya yang tidak diwarisi dari kelas tertentu
Skenario bahwa seseorang akan "secara tidak sengaja" mewarisi dari kelas Anda yang harus disegel agak aneh. Saya juga berpendapat bahwa penilaian individu seseorang tentang apakah "mungkin" untuk mewarisi dari kelas tertentu mungkin agak mencurigakan.
Pada dasarnya ada tiga alasan untuk menyegel kelas: Keamanan, kinerja, dan niat. Dua di antaranya tidak masuk akal dalam TypeScript, jadi kita harus mempertimbangkan kompleksitas vs keuntungan dari sesuatu yang hanya melakukan 1/3 pekerjaan yang dilakukannya di runtime lain.
belum mendengar argumen yang meyakinkan mengapa TS tidak membutuhkannya

Sekedar mengingatkan bahwa ini bukan cara kerjanya. Semua fitur dimulai dari -100 https://blogs.msdn.microsoft.com/ericgu/2004/01/12/minus-100-points/ . Dari postingan itu

Dalam beberapa pertanyaan tersebut, pertanyaan tersebut menanyakan mengapa kita “mengeluarkan” atau “meninggalkan” fitur tertentu. Kata-kata itu menyiratkan bahwa kami memulai dengan bahasa yang ada (C++ dan Java adalah pilihan populer di sini), dan kemudian mulai menghapus fitur hingga kami mencapai titik yang kami sukai. Dan, meskipun mungkin sulit bagi sebagian orang untuk percaya, bukan itu cara bahasa dirancang.

Kami memiliki anggaran kompleksitas yang terbatas. Semua yang kami lakukan membuat setiap hal berikutnya yang kami lakukan 0,1% lebih lambat. Saat Anda meminta fitur, Anda meminta agar setiap fitur lainnya menjadi sedikit lebih sulit, sedikit lebih lambat, sedikit lebih mungkin. Tidak ada yang masuk ke sini karena Java memilikinya atau karena C# memilikinya atau karena hanya beberapa baris kode atau Anda dapat menambahkan sakelar baris perintah. Fitur perlu membayar untuk diri mereka sendiri dan untuk seluruh beban mereka di masa depan.

Apa yang akan mengubah pikiran kita di sini? Orang-orang muncul dengan contoh kode yang tidak berfungsi sebagaimana mestinya karena kata kunci terakhir tidak ada.

Skenario di sini yang coba ditangani oleh kata kunci adalah skenario di mana seseorang secara keliru mewarisi dari suatu kelas. Bagaimana Anda secara keliru mewarisi dari suatu kelas? Bukan kesalahan yang mudah untuk dilakukan. Kelas JavaScript secara inheren cukup rapuh sehingga setiap subkelas harus memahami persyaratan perilaku kelas dasarnya, yang berarti menulis beberapa bentuk dokumentasi, dan baris pertama dari dokumentasi tersebut dapat berupa "Jangan subkelas kelas ini". Atau mungkin ada pemeriksaan runtime. Atau konstruktor kelas dapat disembunyikan di balik metode pabrik. Atau sejumlah opsi lainnya.

Dengan kata lain: satu-satunya proses yang benar untuk mewarisi dari kelas dalam JavaScript selalu dimulai dengan membaca dokumentasi kelas itu. Ada terlalu banyak kendala yang mungkin harus diturunkan oleh kelas dasar dan memulai pengkodean. Jika, pada langkah pertama, Anda seharusnya sudah tahu untuk tidak mencoba, nilai apa yang diberikan penegakan statis pada langkah ketiga?

Mengapa TypeScript harus memiliki kata kunci ini ketika situasinya sudah menjadi satu di mana Anda seharusnya mengalami setidaknya dua penghalang jalan terpisah yang memberi tahu Anda untuk tidak berada di sana sejak awal?

Saya telah memprogram selama kira-kira selama bertahun-tahun sekarang dan saya belum pernah mencoba mensubkelaskan sesuatu hanya untuk mengetahui itu disegel dan saya seharusnya tidak mencoba. Itu bukan karena aku super jenius! Itu hanya kesalahan yang sangat tidak biasa untuk dilakukan, dan dalam JavaScript situasi ini sudah menjadi situasi di mana Anda harus mengajukan pertanyaan tentang kelas dasar di tempat pertama. Jadi masalah apa yang sedang diselesaikan?

Jika Anda tidak tertarik untuk memperkuat fitur utama TypeScript, ucapkan dengan lantang agar berita teknologi dapat menyampaikannya di Twitter

Kami sangat eksplisit tentang bagaimana kami mendesain bahasa https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals ; non-gol nomor satu berbicara persis dengan saran ini.

Reaksi pribadi saya setelah membaca banyak komentar di sini adalah bahwa ada efek bias yang kuat karena konstruksi yang ada dalam bahasa lain. Jika Anda hanya mendekati ini dari perspektif batu tulis kosong, ada lusinan perilaku yang dapat Anda bayangkan, di mana TypeScript hanya memiliki sebagian kecil.

Anda dapat dengan mudah membayangkan beberapa kata kunci yang diterapkan pada suatu metode dan menerapkan metode turunan yang memanggilnya melalui super https://github.com/Microsoft/TypeScript/issues/21388 . Jika C# dan Java memiliki kata kunci ini, orang akan benar-benar menerapkan kata kunci itu di tempat yang masuk akal. Faktanya, ini bisa dibilang jauh lebih umum ditegakkan daripada final, karena tidak mungkin untuk menerapkan pemeriksaan runtime, dan merupakan aspek yang jauh lebih halus dari kontrak turunan dasar daripada "kelas turunan mungkin tidak ada". Ini akan berguna dalam berbagai cara yang tidak akan menjadi final. Saya lebih suka memiliki kata kunci itu daripada yang ini (meskipun saya pikir tidak ada yang memenuhi kompleksitas vs bilah nilai).

Jadi, apa alasan yang tepat untuk mempertimbangkan penerapan ini?

Ketika kita melihat umpan balik, pertimbangan yang kuat terlihat seperti ini:

Saya tidak dapat secara memadai mengekspresikan perilaku perpustakaan JavaScript ini
Kode saya dikompilasi, tetapi seharusnya tidak karena memiliki kesalahan (halus) ini
Maksud dari kode ini tidak dikomunikasikan secara memadai oleh sistem tipe
final menyentuh ini, tetapi begitu juga pengubah yang mengatakan "Fungsi ini tidak dapat dipanggil dua kali berturut-turut" atau "Konstruksi kelas ini tidak benar-benar selesai sampai centang asinkron berikutnya". Tidak semua yang bisa dibayangkan harus ada.

Kami belum pernah melihat umpan balik seperti "Saya menghabiskan berjam-jam men-debug karena saya mencoba mewarisi dari kelas yang sebenarnya tidak dapat disubklasifikasikan" - seseorang mengatakan itu akan memicu 'wat' karena itu tidak benar-benar bisa dibayangkan. Bahkan jika pengubah ada, itu tidak akan terlalu membantu situasi, karena perpustakaan tidak mendokumentasikan apakah kelas dimaksudkan untuk menjadi final - misalnya adalah fs.FSwatcher https://nodejs.org/api/fs.html#fs_class_fs_fswatcher final ? Sepertinya bahkan penulis simpul tidak tahu. Jadi final sudah cukup jika penulis tahu bahwa itu final, tapi itu akan didokumentasikan terlepas dari itu, dan kurangnya final tidak benar-benar memberi tahu Anda apa pun karena seringkali tidak diketahui dengan cara apa pun.


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub https://github.com/Microsoft/TypeScript/issues/8306#issuecomment-458268725 , atau bisukan utasnya https://github.com/notifications/unsubscribe-auth/AA3NA4o9wu54CcJyK1C8WHVjXIQwfms .

Ya, diskusinya begitu menyebar sekarang. Saya ingin melihat masalah ini di pelacak tempat Anda dapat memilih, seperti yang digunakan untuk masalah IntelliJ / webstorm. Dapatkan beberapa angka aktual tentang berapa banyak orang yang ingin melihat final untuk kelas dan/atau metode.

akhir akan sangat membantu

Kata kunci abstrak digunakan untuk mengingatkan pemrogram bahwa mereka harus menimpa bidang tersebut. Kata kunci untuk mengingatkan mereka agar tidak menimpanya akan membantu dengan cara yang sama.

Metode akhir dapat menjadi sangat penting untuk membuat kode lebih terstruktur dan/atau aman.

Misalnya, Anda memiliki aplikasi yang mendukung plugin pihak ke-3. Anda dapat memaksa semua plugin untuk memperluas kelas abstrak yang mengimplementasikan antarmuka untuk metode apa pun yang HARUS mereka buat dan berisi beberapa metode terakhir yang mengontrol urutan operasi dan memungkinkan Anda untuk mengimplementasikan kait di antara operasi. Karena itu, aplikasi Anda tidak perlu mengetahui secara spesifik setiap plugin individual, dan masih mengetahui beberapa status internalnya.

Tanpa metode akhir, mereka dapat mengganggu fungsi Anda yang mengontrol urutan operasi; berpotensi menyebabkan bug, eksploitasi, atau perilaku tak terduga lainnya dalam aplikasi Anda. Sementara sebagian besar situasi akan memiliki beberapa jenis solusi, solusi tersebut bisa rumit, berulang, dan terkadang tidak dapat diandalkan; sedangkan metode akhir menjadikannya salah satu solusi sederhana ketika metode didefinisikan.

Juga, Meskipun contoh ini khusus untuk plugin, ini juga berlaku untuk situasi lain. (pengembang baru di tim, memastikan logika tertentu tidak kehabisan ruang lingkup, menstandardisasi cara kode dijalankan, memastikan metode berbasis keamanan tidak diubah, dll. semua bisa mendapat manfaat dari ini)

@RyanCavanaugh Meskipun ada contoh bagus dengan render() yang menginginkan final, tidak ada yang menyebutkan prinsip buka-tutup (sekarang disingkat di tempat kedua sebagai bagian dari SOLID).
Untuk menulis kerangka kerja yang akan digunakan orang lain, ini akan berguna.
Jika tidak, apa pendekatan yang lebih disukai untuk mengambil pendekatan terbuka-tertutup di TypeScript?

Saya akan MENYUKAI final untuk metode (dan saya kira properti dan deklarasi secara umum). Ini menyebabkan masalah nyata - orang-orang terus menimpa barang-barang secara tidak sengaja tanpa menyadari bahwa ada satu di bawahnya... merusak barang-barang...

Saya juga berpikir bahwa metode terakhir akan sangat berharga, dan seperti yang telah ditunjukkan orang lain, semua argumen yang menentang tampaknya berfokus pada kelas akhir. Ada masalah terpisah (#9264) tetapi ditutup karena tampaknya masalah ini mencakupnya, tetapi saya rasa tidak. Bisakah kita membuka kembali masalah itu, atau mengatasinya dengan benar di sini?

Situasi saya mirip dengan @ 0815fox di mana saya memiliki versi metode "dalam", yang abstrak, tetapi saya ingin versi metode "asli" tidak pernah diganti, karena mengandung perilaku penting dan saya tidak ingin subclass untuk menimpa yang itu.

Saya merasa bingung bahwa tidak peduli dukungan luar biasa dari komunitas yang mendukung fitur ini, itu masih ditolak oleh pengembang TypeScript ...

mungkin stop gap adalah menambahkan standar ke TS-Doc di mana disarankan agar seseorang tidak mewarisi kelas tertentu atau mengganti metode tertentu. Seperti anotasi @final atau yang serupa.

Nah, apa yang harus saya tambahkan ke apa yang semua orang lain, suara untuk "final", katakan ... Ini suara saya untuk itu.

Sepakat. Tentu, Anda dapat menggunakan readonly dengan metode jika Anda akan memperlakukannya seperti properti dari fungsi tipe, tetapi solusi ini bisa sangat membingungkan. Akan menyenangkan untuk memiliki solusi asli untuk masalah ini.

Saya pikir final , terutama final method penting dalam pengecekan tipe, karena sering mengontrol alur kerja metode, yang menjamin urutan metode yang disebut dalam pola Template atau pola desain lainnya.

final harus menjadi cara untuk pergi

Gang - karena tiket ini (a) ditutup, dan (b) memiliki judul yang menyesatkan untuk diskusi "metode terakhir", saya memutuskan untuk membuat tiket baru yang hanya berfokus pada metode akhir . Mudah-mudahan itu akan lebih sulit untuk diabaikan dan bahkan dapat merangsang beberapa kemajuan. Oh ya lihat https://github.com/microsoft/TypeScript/issues/33446

@RyanCavanaugh

kompleksitas vs bilah nilai

Bisakah Anda menguraikan apa yang begitu rumit tentang final ? Sepertinya saya seperti itu akan menjadi salah satu kata kunci termudah untuk diterapkan, terutama mengingat kata kunci seperti abstract sudah diterapkan, dan banyak logika/kode dapat digunakan kembali dari itu.

@vinayluzrao

Sama seperti contoh acak, saat ini kami mengatakan bahwa Anda dapat mewarisi dari apa pun yang memiliki tanda tangan konstruk:

// OK
declare const SomeParent: new() => { a: string };
class A extends SomeParent { }

Jelas kelas final adalah new -mampu:

function make<T>(ctor: new() => T) {
  return ctor;
}
final class Done {
}
// No problem
const d = make(Done); // d: Done

Tapi sekarang ada kontradiksi: Kami memiliki sesuatu yang new -mampu, tetapi tidak sah untuk dimasukkan ke dalam klausa extends . Jadi satu tingkat tipuan mengalahkan final :

function subvertFinal(ctor: new() => any) {
  class Mwhahaah extends ctor {
    constructor() {
      super();
      console.log("I extended a final class! So much for 'types'!")
    }
  }
}
final class Done {
}
subvertFinal(Done);

Ini sudah membuat orang tersandung pada abstract dan orang-orang terus-menerus berdebat bahwa kelas abstract harus memiliki beberapa tanda tangan konstruk implisit yang ada untuk tujuan pengecekan tipe tetapi sebenarnya tidak dapat dipanggil atau semacamnya.

FYI 90% dari waktu ketika kami mengatakan "kompleksitas" dalam diskusi ini, yang kami maksud adalah kompleksitas konseptual seperti yang dialami oleh pengguna TypeScript , bukan kompleksitas implementasi dari pihak kami sebagai pengembang produk. Yang terakhir adalah sesuatu yang dapat ditangani (tipe bersyarat dan tipe yang dipetakan sangat kompleks dari perspektif implementasi, misalnya); kompleksitas konseptual bukanlah sesuatu yang dapat Anda tangani dengan melemparkan lebih banyak insinyur yang lebih cerdas ke produk. Setiap kali kita menambahkan sesuatu ke bahasa yang menyebabkan perilaku yang tidak terduga atau sulit dijelaskan, itu merupakan beban mental bagi setiap pengguna bahasa tersebut.

Saya hanya perlu final untuk metode, karena secara relatif teratur, seseorang menimpa (tanpa menyadarinya) metode di bawahnya, oleh karena itu kode saya tidak berjalan, dan banyak hal pecah dengan cara yang sangat aneh, dan orang tidak tahu mengapa, dan seringkali membutuhkan waktu lama untuk debug.

@RyanCavanaugh yang benar-benar menjelaskannya, terima kasih. Padahal, saya ingin membuang pemikiran: bagi saya tampaknya masalah dalam contoh yang Anda berikan muncul karena kopling yang kuat antara new dan extends , mungkin itu masalah yang lebih besar , dan bukan final itu sendiri. Saya bertanya-tanya mengapa keputusan penggabungan ini dibuat, dan betapa sulitnya untuk membatalkannya (sama sekali tidak menyiratkan bahwa itu harus dibatalkan) pada saat ini.

Saya tidak yakin apakah ini mungkin diskusi yang terlalu tangensial untuk dilakukan di sini.

Ini adalah masalah yang saya hadapi. Contoh kode sederhana seperti itu tidak dapat dikompilasi.
final kata kunci SEMPURNA akan menyelesaikannya:

abstract class Parent {
    public abstract method(): this;
}

class Child extends Parent {
    public method(): this {
        return new Child();
    }
}

Ketik 'Anak' tidak dapat ditetapkan untuk mengetik 'ini'.
'Anak' dapat ditetapkan ke batasan tipe 'ini', tetapi 'ini' dapat dipakai dengan subtipe batasan 'Anak' yang berbeda.

Tempat bermain

tolong ini bisa dibuka kembali? jelas bahwa ada kebutuhan untuk fitur ini

Kami telah membahas masalah ini sekitar selusin kali di berbagai pertemuan dan setiap kali memutuskan untuk tidak melakukannya. Membuka kembali ini untuk membahas yang ketiga belas kali bukanlah penggunaan waktu kita yang baik.

Anda dapat tidak setuju dengan keputusan tim TypeScript, yang baik-baik saja, tetapi kami tidak dapat menghabiskan sepanjang hari berputar-putar untuk menentukan kembali keputusan yang tidak disetujui orang. Ada ribuan saran lain dalam repo ini yang layak untuk dipertimbangkan terlebih dahulu sebelum yang satu ini diangkat lagi.

Diskusi konstruktif seperti komentar @nicky1038 sangat diterima disini; Saya akan meminta agar orang-orang tidak terus-menerus melakukan ping ke utas ini untuk permintaan berita/pertimbangan ulang sehingga kami tidak perlu menguncinya.

Saya di sini untuk mendukung penambahan final untuk metode juga. Saya memiliki kelas dasar dengan fungsi yang disebut _init_ yang saya tidak ingin subkelas dapat menimpanya. Final akan menjadi solusi sempurna.

Saya juga mendukung penambahan kata kunci final untuk metode.
Saat ini adalah standar dalam pemrograman berorientasi objek.

Menambahkan final masuk akal bagi saya. Terutama ketika membangun perpustakaan/kerangka kerja untuk digunakan orang lain, di mana Anda bahkan tidak ingin membiarkan risiko menimpa suatu fungsi.

Jika saya tidak salah menambahkan final dapat membantu menghindari kerumitan dengan kesalahan 'myObj' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'MyClass' saat bekerja dengan obat generik karena saya dapat menghasilkan MyClass final dan karenanya menjamin di sana tidak ada subtipe lainnya.
Tidak 100% yakin tetapi saya pikir itulah cara Anda dapat menyelesaikan masalah ini di Jawa.

Kami memiliki hal yang sama, kami membuat Komponen dasar dalam sudut yang dapat atau perlu diperluas oleh banyak paket lain sebagai basis di sana.
Dalam hal itu kami memiliki hal-hal seperti ngOnChanges atau bahkan ngOnInit dari sudut yang hanya dapat diimplementasikan oleh kelas dasar dan subkelas tidak boleh menimpanya tetapi benar-benar mengimplementasikan ngOnInitReal (atau apa yang kami sebut itu) sehingga kami sepenuhnya memegang kendali saat itu dipanggil dan dengan apa.
Karena kami tidak dapat mengontrol ini, kami hanya dapat melakukan ini melalui dokumentasi. Jika salah satu pembuat komponen tidak membaca ini, komponennya dapat rusak, atau perlu menyalin banyak kode dari basis kami untuk melakukan hal yang sama..

Jadi tanpa metode akhir, kami tidak dapat benar-benar membuat kontrak api yang kokoh.

apakah seseorang menyebutkan

class Fancy {
    readonly secureFoo = (message: string) => {
        console.log(message);
    }
}

apakah seseorang menyebutkan

class Fancy {
    readonly secureFoo = (message: string) => {
        console.log(message);
    }
}

Meskipun Anda dapat melakukan ini, ada beberapa perbedaan. Misalnya, setiap instance kelas akan memiliki salinan fungsinya sendiri, bukan hanya prototipe yang memiliki fungsi yang digunakan semua orang.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat