- Mengemukakan perlunya TypeScript menghasilkan informasi tipe runtime, serta mengumpulkan daftar Type Mapping, Code Generation / External Tool, dan proyek Adapter yang digunakan untuk mengakali masalah ini
- Masalah intinya adalah bahwa tanpa sistem tipe reflektif, menangani serialisasi dan validasi membutuhkan boilerplate tanpa akhir atau pembuatan kode khusus berbasis berkas skema
- Sebagai solusi sementara, diajukan io-ts, zod, dan lain-lain, tetapi ini merepotkan karena tipe harus dideklarasikan ulang dengan cara masing-masing pustaka, dan pustaka tersebut juga tidak dapat mendukung semua fitur tipe TypeScript
- Sambil mengakui bahwa penghapusan tipe di TypeScript punya keunggulan karena proyek JavaScript dapat memakai JavaScript hasil keluaran tanpa pengetahuan TypeScript, artikel ini berpendapat bahwa informasi tipe bisa dipancarkan dalam bentuk tabel lookup yang terpisah dari kode
- Disertai permintaan agar jangan menyelesaikannya dengan dekorator, artikel ini mengusulkan pendekatan seperti fungsi orde tinggi yang dikenali compiler semacam
typescript.generateRuntimeType<T>(), F# Type Providers, dan C# Source Generators untuk mendukung penggunaan interface dan tipe dari pustaka eksternal - Menghubungkan issue GitHub berusia 8 tahun sebagai diskusi terkait yang sudah ada, dan meminta agar jika ada proyek lain yang mengalami masalah serupa, silakan kirim PR untuk ditambahkan ke daftar, terlepas dari solusi yang dipakai
1 komentar
Opini Hacker News
Saya harus membaca penjelasan masalah sekitar empat kali baru paham apa yang diminta, dan kasus seperti ini menunjukkan kenapa penulisan yang ringkas itu penting.
Kebutuhan sebenarnya tampaknya adalah keamanan tipe saat runtime, tetapi TypeScript sejak lama menarik garis bahwa ia tidak akan menjadi runtime pengganti/tambahan untuk JavaScript, jadi kemungkinannya terlihat kecil. TypeScript berperan mengompilasi ke JS lalu selesai, dan apa pun yang terjadi setelah itu di V8 dan lain-lain berada di luar cakupannya.
Permintaan agar “TypeScript mengekspor informasi tipe runtime” rasanya lebih dekat ke permintaan untuk produk baru yang cukup berbeda dari TypeScript saat ini.
Misalnya, akan bagus jika bisa membuat fungsi
validategenerik yang menerima interface dan objek arbitrer lalu memverifikasinya. Mungkin bisa dengan menghasilkan kode validasi JS per tipe lewat refleksi waktu kompilasi, atau dengan meneruskanTsebagai argumen saat runtime untuk dibandingkan, tetapi dalam filosofi TypeScript saat ini hal itu tampak sulit.TypeScript mengetahui banyak informasi tipe saat kompilasi, tetapi setelah kompilasi selesai informasi itu dibuang. Informasi ini sebenarnya bisa diekspor ke file atau disimpan sebagai metadata di
Reflect, jadi sayang sekali jika dibuang, terutama mengingat sifat JavaScript di mana “semuanya adalah objek”.Meski tidak sampai memperoleh keamanan tipe TS yang benar-benar akurat, kita tetap bisa menambahkan sebagian pemeriksaan lewat getter/setter atau ES2015
Proxy, dan jika informasi tipe dibiarkan tetap ada saat runtime, banyak kemungkinan menarik akan terbuka sebagai metadata yang dapat dieksekusi.Saat ini
enumTS memang sudah dikeluarkan sebagai objek JS sehingga bisa di-query saat runtime, tetapi union type literal string tidak demikian. Selain itu, TypeScript juga mendukung fungsi type guard, jadi tampaknya tidak terlalu sulit untuk menghasilkan fungsi semacam itu secara otomatis dengan memanfaatkan informasi di dalam sistem tipe.Anggap saja analoginya seperti file PDB. Karena TypeScript sudah memiliki informasi itu lalu membuangnya, ini bukan berarti harus membuat produk yang sepenuhnya baru.
https://github.com/microsoft/TypeScript/issues/3628
Dari sudut pandang PM TypeScript, saya paham keinginan ini. Untuk validasi data, pemeriksaan tipe runtime memang sering dibutuhkan, dan ada banyak library untuk menutup celah itu.
Namun, fakta bahwa ada banyak library dengan keputusan desain yang berbeda-beda justru menandakan bahwa ini bukan masalah yang sudah selesai dengan satu jawaban yang jelas. Saat perancangan awal TypeScript, hal ini juga sudah dipahami, dan menurut saya prinsip itu bertahan dengan baik.
Sebagai gantinya, TypeScript kini sudah cukup kuat untuk mengekspresikan secara akurat dalam bentuk tipe apa yang sebenarnya dilakukan library pemeriksaan tipe runtime, dan pengguna bisa menyusun logika validasi runtime dari tipe melalui API. Tingkat fleksibilitas seperti ini terasa masuk akal.
Misalnya, saya membayangkan bahasa yang memungkinkan mendefinisikan tipe sebagai angka dalam rentang tertentu atau string yang cocok dengan pola kode pos tertentu, lalu compiler memeriksa validitasnya dengan memakai fungsi validasi yang ditulis seperti fungsi biasa. Saya penasaran apakah ketiadaan fitur seperti ini adalah keputusan desain untuk menghindari kompleksitas yang tidak perlu, atau karena kendala teknis seperti performa.
Saat ini pun orang yang membutuhkannya bisa membuat preprocessor yang menghasilkan objek runtime dari informasi tipe sebelum diteruskan ke
tsc, tetapi upayanya tersebar dan tidak seragam. Jika ada preprocessor model plugin resmi beserta ekosistemnya, mungkin solusi yang baik bisa ditemukan.Begitu ada pemicunya, komunitas kemungkinan akan membantu pemeliharaannya, dan itu bisa menyelesaikan berbagai kebutuhan code generation, dari pembuatan client sampai assertion tipe runtime.
ClassAda alasan mengapa ini tidak dilakukan. TypeScript akan menjadi semacam runtime di atas JavaScript, dan berubah menjadi bahasa baru yang dikompilasi ke JS
Saat ini TypeScript lebih dekat ke JavaScript yang diberi anotasi tipe. Sudah banyak bahasa yang dikompilasi ke JS, jadi pakai saja salah satunya. Orang yang menginginkan TypeScript dengan tipe runtime tampaknya ingin menulis kode bergaya Java/OOP alih-alih JavaScript, tetapi JavaScript adalah bahasa bertipe dinamis, dan itu juga merupakan kelebihannya
generateTypeInfo!()diperluas menjadi objek JS yang mengenkode tipeFoo, itu tetap akan dikompilasi menjadi JavaScript yang mudah dibacaHanya saja macro ini merusak sifat “TS = JS dengan anotasi tipe dan kompilasi cukup dengan menghapus anotasi”. Karena struktur tipe
Fooharus benar-benar dihitung.Selain itu, tipe TypeScript bersifat struktural, jadi dengan memeriksa struktur objek saat runtime kita bisa mengetahui tipe sampai taraf tertentu, tetapi jika kita meminta informasi nominal yang sudah dihapus seperti apakah sesuatu termasuk dalam union string atau nama tipenya, itu cepat menjadi masalah sulit karena sistem tipe TypeScript yang Turing-complete dan konversi struktural implisit
Bicara serius, bahkan tanpa runtime pun Anda bisa mendapatkan refleksi dengan mengekspos tipe sebagai data. Melihat banyaknya pengembang yang berusaha meniru ini, tidak melakukannya justru terlihat kurang bijak
Tipe runtime menyelesaikan masalah harus memelihara secara terpisah dan berulang sistem tipe untuk kompilasi dan sistem tipe untuk validasi data, dan tampaknya tidak berhubungan langsung dengan OOP.
io-ts, yang sering disukai sebagai library workaround untuk masalah ini, juga sangat bertumpu pada pemrograman fungsionalNaNDalam alur kerja yang saya lihat, TypeScript sudah merupakan bahasa yang dikompilasi ke JS, jadi sekalian saja memanfaatkan kelebihan itu semaksimal mungkin
Ia lebih dekat ke anotasi tipe + Babel, dan menambahkan satu fitur yang sangat berguna di sini adalah ide bagus. Aneh rasanya tidak bisa dengan aman mengubah string hasil
JSON.parsemenjadi struktur bertipe, sementara bahasa lain umumnya bisaSebelum isi di bawah ini, ini adalah topik yang valid dan cukup layak diperdebatkan, dan saya tidak menganggap ada satu jawaban yang objektif benar
TypeScript adalah lapisan opsional di atas JavaScript, kecuali pengecualian bahwa
Enummengekspor objek, dan kode TS menjadi JS jika tipenya saja dihapus. Selama tidak terlalu menyimpang dari prinsip ini, kebutuhan nyatanya lebih mirip library yang membuat serializer/validator dari tipe. Library seperti itu sudah banyak, dan pada akhirnya tuntutannya terlihat seperti menjadikan salah satunya sebagai opsi standar resmi.Secara pribadi saya tidak ingin refleksi runtime masuk ke bahasa inti TypeScript. Pada runtime seharusnya hanya ada JavaScript, dan saya menghargai bahwa output JS tetap mudah dibaca dan bisa di-debug bahkan tanpa source map
Enum, ada pengecualian lain yang menghasilkan kode runtime, dan kebanyakan juga dianggap sebagai kekeliruan. Contoh utamanya adalahmoduledannamespaceyang dulu merupakan struktur runtimeNamun dalam beberapa tahun terakhir itu terutama dipakai di dalam TypeScript sendiri, dan belakangan TypeScript juga mulai meninggalkannya. Ada juga pengecualian yang cukup populer, yaitu parameter property, sintaks untuk mendefinisikan tipe anggota kelas dari parameter konstruktor, dan tampaknya lolos dari kontroversi karena mengurangi boilerplate yang berulang
Daripada memohon pada dewa-dewa TypeScript, mungkin lebih baik membuat kesepakatan di sisi JavaScript agar pemeriksaan tipe masuk ke JS
Jika butuh tipe runtime, gunakan type guard, dan jika sering dibutuhkan, gunakan
io-tsatauzoduntuk menulis tipe sebagai validator/codec/schema. Sampai ada cara yang disepakati di JavaScript, saya tidak berpikir spesifikasi TS, type checker, dan komunitasnya harus menanggung validasi runtimezoduntuk menulis schema dan validator lalu menurunkan tipe darinya. Saya suka bahwa validasi ikut berevolusi bersama perubahan tipeAda yang merasa fitur seperti ini harus masuk ke bahasa, tetapi karena tiap library punya banyak pilihan desain yang bisa diperdebatkan, mungkin justru lebih baik bisa memilih implementasi sesuai kebutuhan proyek.
Namun pola seperti validasi runtime
zoddan penurunan tipe berbasis schema tidak selalu cocok mulus dengan pattern matching milikts-pattern. Definisi tipe library-library ini seperti labirin, dan kadang ada kode yang terasa seharusnya bekerja tetapi tidak berjalan seperti yang diharapkan. Akan sangat bagus jika keamanan runtime dan pattern matching dengan pemeriksaan exhaustiveness bisa digabungkan dengan mulusPromise, decorator, dan operator pipe baru semuanya terasa diarahkan ke implementasi yang keliruSaya ingat salah satu pengembang TypeScript pernah mengatakan bahwa jika mulai lagi dari awal, mereka tidak akan memasukkan
enum. Karenaenumadalah satu-satunya fitur yang menghasilkan kode runtimeTypeScript tidak mengubah perilaku runtime, dan juga tidak punya sesuatu seperti
{#if}khusus TypeScriptMeski begitu, TypeScript memang punya beberapa fitur yang melampaui “JavaScript + anotasi tipe”. Ada
namespace, sintaks untuk mendefinisikan properti kelas dari argumen konstruktor, decorator eksperimental lama, dan sintaks parameterthisyang hilang saat kompilasi tetapi terlihat seperti lebih dari sekadar menambahkan anotasi tipe pada fungsi JSJudul yang lebih baik mungkin lebih dekat ke “TypeScript, tolong sediakan refleksi/tipe runtime”
Solusi terbaik saat ini mungkin adalah
emitDecoratorMetadata. https://www.typescriptlang.org/tsconfig#emitDecoratorMetadataPenulis tampaknya salah memahami tujuan desain TypeScript. Tujuannya bukan menghasilkan output JS yang bersih tanpa kompleksitas, melainkan memastikan semantik runtime TypeScript tetap sama dengan JavaScript.
Kecuali
enumyang merupakan pengecualian yang disayangkan, TypeScript menjadi JavaScript hanya dengan menghapus anotasi tipe. Ekosistem di sekitar TypeScript bergantung pada penghapusan tipe secara penuh, dan jika tuntutan ini diterima, dukungan TS di ESBuild, Deno, dan Bun bisa menjadi hampir mustahil. Karena masing-masing harus mengimplementasikan ulang seluruhtscdalam bahasa mereka sendiri.Sebaliknya, library kompleks yang dikeluhkan OP diimplementasikan di ruang pengguna, sehingga kompatibel dengan alat-alat tersebut
keyofbisa diubah menjadi daftar kunci kelas, atau kelas TS bisa dibuat menginisialisasi kunci keundefinedseperti kelas ES6Saat ini kelas TypeScript menghapus semua kunci yang tidak didefinisikan secara eksplisit, jadi jika
Object.keys()dipanggil pada instance baru, tidak ada apa pun yang muncul. Jika saja ada opsitsconfig.jsonuntuk mentransformasikan kelas TS seperti kelas ES6, atau sekadar mengizinkan kelas ES6 berdampingan di dalam TS, pembuatan kode akan menjadi jauh lebih mudah.Selain itu, sintaks statis seperti
tstypeof Foo::barakan sangat bagus jika dikompilasi menjadi"string". Bahkan RTTI dasar saja bisa langsung menghilangkan banyak kode boilerplate TypeScript yang berantakanSenang melihat tulisan ini. Saya mulai memakai TypeScript pada 2018, dan setelah sekitar dua tahun mulai percaya bahwa ini pada dasarnya bukan solusi yang lengkap
Saya datang dari Haskell/C#/F#, dan meskipun TS punya sistem tipe yang kuat, di luar sebagian masa pengembangan ia tidak banyak memberi keunggulan yang diberikan bahasa-bahasa tersebut. Saat menangani dunia nyata, secara praktis ia jauh lebih terbatas daripada C#. Jika tidak menyadari bahwa setelah kompilasi ia turun menjadi JS tanpa pemeriksaan, Anda harus selalu mewaspadai abstraksi yang bocor untuk menghindari bug yang seharusnya tidak mungkin terjadi pada alat yang mengklaim punya tipe statis
Jika perlu mengembangkan .NET, gunakan C#; jika perlu mengembangkan web, gunakan TypeScript. C# bertipe nominal dan punya generic yang tereifikasi yang cukup baik, sementara TypeScript bertipe struktural dan mengorbankan soundness sebagai ganti sistem tipe yang kuat untuk mengekspresikan relasi tipe yang kompleks.
Diragukan apakah bahasa yang menggabungkan keduanya akan bagus; kedua bahasa itu bekerja baik ke arah yang berbeda, tetapi arah tersebut tidak terlalu cocok satu sama lain
Saya sangat puas menggunakan https://zod.dev untuk validasi data runtime
Cukup keren karena kita bisa mengekspresikan maksud langsung di tempatnya dengan API yang fasih, tanpa perlu mendefinisikan tipe nominal terpisah
Contoh sederhananya, ini diperlukan untuk membedakan angka
2dan nilai mata uang2dalam domain