TypeScript’in Çalışma Zamanında Tip Bilgisi Üretmesi Gerekiyor
(github.com/akutruff)- TypeScript’in çalışma zamanı tip bilgisi üretmesi gerektiğini savunan ve bu sorunu aşmak için kullanılan Type Mapping, Code Generation / External Tool ve Adapter projelerini bir araya getiren bir liste
- Temel sorun, yansıtıcı bir tip sistemi olmadan serileştirme ve doğrulamanın ele alınmasının sonsuz boilerplate ya da şema dosyası tabanlı özel kod üretimini gerektirmesi
- Geçici çözüm olarak io-ts ve zod gibi araçlar öneriliyor; ancak tiplerin her kütüphane için yeniden tanımlanması gerekiyor ve bu kütüphaneler TypeScript’in tüm tip özelliklerini destekleyemiyor
- TypeScript’in tip silme yaklaşımının, JavaScript projelerinin TypeScript bilgisi olmadan üretilmiş JavaScript’i kullanabilmesini sağlama avantajı kabul edilmekle birlikte, tip bilgisinin koddan ayrılmış bir başvuru tablosu biçiminde de üretilebileceği savunuluyor
- Bunun dekoratörlerle çözülmemesi istenirken, interface kullanımı ve harici kütüphane tip desteği için
typescript.generateRuntimeType<T>()gibi derleyicinin tanıdığı yüksek seviyeli fonksiyonlar, F# Type Providers ve C# Source Generators benzeri yaklaşımlar öneriliyor - İlgili mevcut tartışmalar arasında 8 yıllık GitHub issue bağlantısı veriliyor ve aynı sorunu yaşamış projelerin, çözümden bağımsız olarak listeye eklenmesi için PR göndermesi isteniyor
1 yorum
Hacker News yorumları
Ne istendiğini anlamak için açıklamayı yaklaşık 4 kez okumam gerekti; bu da özlü yazımın neden önemli olduğunu gösteren türden bir örnek
Asıl talep çalışma zamanında tip güvenliği gibi görünüyor, ancak TypeScript uzun süredir JavaScript’in yerine geçen/ona eklenen bir çalışma zamanı olmayacağı çizgisini koruduğu için bunun pek olası olmadığı düşünülüyor. TypeScript’in rolü JS’ye derlemek ve çekilmek; sonrasında V8 vb. üzerinde olanlar kapsam dışı.
“TypeScript çalışma zamanı tip bilgisi yaymalı” talebi, mevcut TypeScript’ten epey farklı yeni bir ürün istemeye daha yakın
reflection) ile değerler üretilmesi ve bu bilginin çalışma zamanında kullanılabilmesi isteniyorÖrneğin rastgele bir arayüz ve nesne alıp doğrulayan genel amaçlı bir
validatefonksiyonu yazabilmek güzel olurdu. Derleme zamanı yansıtmasıyla tipe göre JS doğrulama kodu üretmek ya da çalışma zamanındaT’yi argüman olarak geçirip karşılaştırmak mümkün olabilir, fakat mevcut TypeScript felsefesiyle bunun zor olduğu düşünülüyorTypeScript derleme sırasında tip bilgisi hakkında çok şey biliyor, ama derleme bitince bu bilgiyi atıyor. Bu bilgiyi bir dosyaya dışa aktarabilir ya da
Reflectüzerinde metadata olarak bırakabilirken bunu atmaları, özellikle JavaScript’in “her şey nesnedir” doğası düşünülünce, üzücü bir kısıt gibi geliyor.Birebir TS tip güvenliği elde edilemese bile, getter/setter veya ES2015
Proxyile bazı kontroller eklenebilir; tip bilgisinin çalışma zamanında kalması da çalıştırılabilir metadata olarak birçok ilginç olasılığın önünü açarZaten TS
enumları JS nesneleri olarak çıktılanıyor ve çalışma zamanında sorgulanabiliyor, ama string literal union tipleri böyle değil. Ayrıca TypeScript tip koruma fonksiyonlarını destekliyor; dolayısıyla tip sistemindeki bilgiyi kullanarak bu tür fonksiyonları otomatik üretmek de çok zor görünmüyorBunu PDB dosyaları gibi bir benzetmeyle düşünebilirsiniz. TypeScript’in zaten sahip olup sonra attığı bir bilgi olduğu için tamamen yeni bir ürün gerekmiyor
https://github.com/microsoft/TypeScript/issues/3628
TypeScript PM bakış açısından bu isteği anlıyorum. Veri doğrulamada çalışma zamanı tip denetimi sık sık gerekiyor ve bu boşluğu kapatmaya çalışan çok sayıda kütüphane var
Ancak farklı tasarım kararları alan çok sayıda kütüphanenin bulunması, bu problemin net bir doğru cevabı olan çözülmüş bir mesele olmadığının göstergesi. TypeScript’in ilk tasarım döneminde de bunun farkındaydılar ve bence bu ilke zaman içinde iyi dayandı.
Bunun yerine TypeScript, çalışma zamanı tip denetimi kütüphanelerinin fiilen yaptığı işleri tiplerle doğru ifade edebilecek kadar güçlü hale geldi; kullanıcılar da API’ler üzerinden tiplerden çalışma zamanı doğrulama mantığı kurabiliyor. Bu seviye makul bir esneklik gibi geliyor
Örneğin belirli bir aralıktaki sayılar ya da posta kodu desenini karşılayan string’ler için tip tanımlayabilen ve derleyicinin, normal fonksiyon gibi yazılmış doğrulama fonksiyonlarını kullanarak geçerliliği kontrol ettiği bir dil hayal ediyorum. Böyle bir özelliğin olmaması gereksiz karmaşıklıktan kaçınmaya yönelik bir tasarım kararı mı, yoksa performans gibi teknik kısıtlar mı var, merak ediyorum
Şu anda da buna ihtiyacı olanlar,
tsc’ye vermeden önce tip bilgisinden çalışma zamanı nesneleri üreten bir ön işleyici yazabilir, ama çaba çok dağınık durumda. Resmi, eklenti tabanlı bir ön işleyici ve etrafında bir ekosistem olsa iyi bir çözüm bulunabilirKıvılcım bir kez çaksa topluluk bakımına yardımcı olur ve istemci üretiminden çalışma zamanı tip assertion’larına kadar çeşitli kod üretim ihtiyaçları karşılanabilir
Classnesneleri üzerinde bırakılmasını öneren diğer teklif hakkında ne düşünüldüğünü merak ediyorumBunu yapmamanın bir nedeni var. TypeScript, JavaScript’in üzerinde bir runtime katmanına dönüşür ve JS’ye derlenen yeni bir dil olur
Şu anki TypeScript, tip açıklamaları eklenmiş JavaScript’e daha yakın. JS’ye derlenen pek çok dil zaten var; öyle bir şey isteniyorsa onlardan biri kullanılabilir. Runtime tipi olan bir TypeScript isteyenler, JavaScript’ten çok Java/OOP tarzı kod yazmak istiyor gibi görünüyor; ama JavaScript dinamik tipli bir dil ve bu da onun avantajlarından biri
generateTypeInfo!()gibi bir makro,Footipini kodlayan bir JS nesnesine genişliyorsa yine okunabilir JavaScript’e derlenmiş olurAma bu makro, “TS = tip açıklamalı JS’dir ve derleme sadece açıklamaları silmektir” özelliğini bozar. Çünkü
Foonun yapısal tipini gerçekten hesaplamak gerekir.Ayrıca TypeScript tipleri yapısaldır; bu yüzden runtime’da nesne yapısını inceleyerek tipe dair bir miktar bilgi edinilebilir, ama bir string union’a ait olup olmadığı gibi silinmiş nominal bilgiler veya tip adları istenirse, TypeScript tip sisteminin Turing-tamlığı ve örtük yapısal dönüşümleri nedeniyle bu hızla zor bir probleme dönüşür
Ciddi konuşursak, runtime olmadan da tipleri veri olarak açığa çıkarırsanız reflection elde edebilirsiniz. Pek çok geliştiricinin bunu taklit etmeye uğraşmış olması, bunu yapmamanın daha çok anlamsız göründüğünü düşündürüyor
Runtime tipler, derleme için kullanılan tip sistemi ile veri doğrulama için kullanılan tip sistemini ayrı ayrı ve tekrar ederek sürdürme sorununu çözüyor; bunun OOP ile doğrudan ilgili olduğu da pek görünmüyor. Bu soruna geçici çözüm sunan kütüphaneler arasında tercih ettiğim
io-tsde güçlü biçimde fonksiyonel programlama tarafına yaslanıyorNaNgibi beklenmedik değerlere sürekli takılıp düşülen, iç içe geçmiş bir felakete yakınlarBenim gördüğüm iş akışlarında TypeScript zaten JS’ye derlenen bir dil, dolayısıyla madem öyle, bu avantajı olabildiğince kullanmak daha iyi
Tip açıklamaları + Babel’e daha yakın; buna çok faydalı bir özellik daha eklemek iyi bir fikir.
JSON.parseile elde edilen bir stringi tipli bir yapıya güvenli biçimde dönüştürememek tuhaf ve diğer dillerde bu genelde mümkünAşağıdakilere geçmeden önce: bu, tartışmaya fazlasıyla değer geçerli bir konu ve burada nesnel olarak tek bir doğru cevap olduğunu düşünmüyorum
TypeScript,
Enumun nesne export etmesi istisnası dışında, JavaScript üzerinde isteğe bağlı bir katmandır ve TS kodu tipler silinince JS olur. Bu ilkenin çok dışına çıkılmadığı sürece, asıl talep tiplerden serializer/validator üreten bir kütüphaneye daha yakın görünüyor. Böyle kütüphaneler zaten çok ve sonuçta talep, bunlardan birinin resmî standart seçenek haline getirilmesi gibi duruyor.Kişisel olarak TypeScript’in çekirdek diline runtime reflection gelmesini istemem. Runtime’da yalnızca JavaScript olmasını, source map olmadan bile üretilen JS’nin okunabilir ve debug edilebilir kalmasını değerli buluyorum
Enumdışında da runtime kodu üreten istisnalar var ve bunların çoğu da yine hata olarak görülüyor. En belirgin örneklermodulevenamespace; bunlar runtime yapılarıydıYine de son birkaç yılda bunlar daha çok TypeScript’in kendi içinde kullanıldı ve TypeScript de son dönemde bundan uzaklaşıyor. Bir diğer oldukça popüler istisna da parameter properties; constructor parametrelerinden sınıf üyesi tiplerini tanımlayan bir sözdizimi ve tekrarlı boilerplate’i azalttığı için daha az tepki çekmiş gibi görünüyor
TypeScript tanrılarına yalvarmak yerine, JavaScript tarafıyla anlaşıp tip kontrolünü JS’ye koymak daha iyi olmaz mı diye düşünüyorum
Runtime tipi gerekiyorsa type guard kullanılır; sık gerekiyorsa da
io-tsveyazodile tipler validator/codec/schema olarak yazılır. JavaScript için üzerinde uzlaşılmış bir yöntem oluşana kadar TS spesifikasyonunun, tip denetleyicisinin ve topluluğun runtime doğrulamayı sırtlanması gerektiğini düşünmüyorumzodile schema ve validator yazıp oradan tip türetme yaklaşımından oldukça memnunum. Doğrulamanın tip değişimleriyle birlikte evrilmesi hoşuma gidiyorBazıları bunun dilin içine girmesi gerektiğini düşünüyor, ama kütüphaneler arasında tartışmalı birçok tasarım tercihi var; bu yüzden uygulamayı proje ihtiyaçlarına göre seçmek aslında daha iyi olabilir.
Yine de
zodruntime doğrulaması ve schema tabanlı tip türetme gibi kalıplar,ts-patterniçindeki pattern matching ile her zaman iyi uyuşmuyor. Bu kütüphanelerin tip tanımları labirent gibi ve çalışacakmış gibi görünen kod bazen beklendiği gibi davranmıyor. Runtime güvenliği ile exhaustiveness checking pattern matching pürüzsüz biçimde birleşebilse çok iyi olurduPromise, decorator’lar ve yeni pipe operatörünün hepsinde, uygulamanın gittiği yönün yanlış olduğunu hissediyorumTypeScript geliştiricilerinden birinin, yeniden başlasalardı
enumeklemeyeceklerini söylediğini hatırlıyorum. Çünküenum, runtime kodu üreten tek özellikTypeScript runtime davranışını değiştirmez ve TypeScript’e özel
{#if}benzeri bir şey de yokturYine de TypeScript’te “JavaScript + tip açıklamaları”nın ötesine geçen birkaç özellik var.
namespace, constructor argümanlarında sınıf property’lerini tanımlama sözdizimi, eski deneysel decorator’lar ve derleme sırasında kaybolsa da JS fonksiyonlarına yalnızca tip açıklaması eklemekten fazlası gibi görünenthisparametresi sözdizimi bunlardan bazılarıDaha iyi bir başlık, “TypeScript, reflection/runtime tipleri sağlasın” olurdu
Şu an en iyi çözüm muhtemelen
emitDecoratorMetadata. https://www.typescriptlang.org/tsconfig#emitDecoratorMetadataYazarın TypeScript'in tasarım hedefini yanlış anladığı görülüyor. Amaç, karmaşıklık içermeyen temiz JS çıktısı üretmek değil, TypeScript'in çalışma zamanı anlambiliminin JavaScript ile aynı kalmasını sağlamak.
Talihsiz bir istisna olan
enumdışında, TypeScript tür ek açıklamalarının kaldırılmasıyla JavaScript'e dönüşür. TypeScript etrafındaki ekosistem tamamen tür silmeye dayanır ve bu talep kabul edilirse ESBuild, Deno ve Bun'ın TS desteği neredeyse imkansız hale gelebilir. Çünkü her birinintsc'nin tamamını kendi dillerinde yeniden uygulaması gerekir.Öte yandan OP'nin şikayet ettiği karmaşık kütüphaneler kullanıcı alanında uygulanmıştır, bu yüzden bu araçlarla uyumludur
keyof, sınıf anahtarları listesine dönüştürülebilir ya da TS sınıfları ES6 sınıfları gibi anahtarlarıundefinedile başlatacak şekilde ayarlanabilirŞu anda TypeScript sınıfları açıkça tanımlanmamış tüm anahtarları kaldırıyor; bu yüzden yeni bir örnekte
Object.keys()çağırırsanız hiçbir şey çıkmıyor. TS sınıflarını ES6 sınıfları gibi dönüştüren birtsconfig.jsonseçeneği ya da ES6 sınıflarına TS içinde birlikte izin verilmesi bile kod üretimini çok daha kolay hale getirir.Ayrıca
tstypeof Foo::bargibi statik bir sözdizimi"string"olarak derlense harika olurdu. Temel düzeyde RTTI bile olsa, dağınık boilerplate TypeScript kodunun önemli bir kısmı anında ortadan kalkabilirBu yazıyı görmek sevindirici. 2018'de TypeScript'e başladım ve yaklaşık 2 yıl sonra bunun özünde tam bir çözüm olmadığına inanmaya başladım
Haskell/C#/F# geçmişim var ama TS, güçlü bir tür sistemine sahip olsa da geliştirme sürecinin bazı bölümleri dışında bu dillerin sağladığı avantajların çoğunu vermiyor. Gerçek dünyayla uğraşırken pratikte C#'tan çok daha sınırlı. Derlemeden sonra doğrulama olmadan JS'ye düştüğünü bilmiyorsanız, statik tip iddia eden bir araçta imkansız olması gereken hatalardan kaçınmak için sızdıran soyutlamaların her zaman farkında olmanız gerekiyor
.NET geliştirmeniz gerekiyorsa C# kullanın, web geliştirmeniz gerekiyorsa TypeScript kullanın. C# nominal tiplidir ve oldukça iyi yenidenleştirilmiş generics'e sahiptir; TypeScript ise yapısal tiplidir ve soundness'tan ödün vererek karmaşık tür ilişkilerini ifade eden güçlü bir tür sistemi sunar.
İkisini birleştiren bir dilin iyi olup olmayacağı şüpheli; iki dil farklı yönlerde iyi çalışıyor ama bu yönler birbiriyle pek uyumlu değil
Çalışma zamanı veri doğrulaması için https://zod.dev kullanmaktan tamamen memnun kaldım
Ayrı nominal tipleri ayrıca tanımlamak zorunda kalmadan, akıcı bir API ile niyeti doğrudan yerinde ifade edebilmek oldukça hoşuma gitti
Basit bir örnek olarak, bir etki alanında sayı
2ile para tutarı2'yi ayırt etmek için buna ihtiyaç duyarsınız