React & TypeScript
TypeScript; JS temelli geliştirme alanlarında oldukça tercih edilen bir mimari ve React’da da kullanım açısından önde geliyor. Peki TypeScript’in tam olarak bize getirisi ne? Proje geliştirme sürecinde ne gibi katkıları bulunmakta ve bize sunduğu avantajlar neler?
Gelin hep beraber bir bakalım! ☕️
Öncelikle; yazı TypeScript’e giriş tarzında olmayacak, çünkü bunun için zaten çok fazla kaynak bulunmakta ve çok kapsamlı bir konu. Ben ağırlıklı olarak öne çıkan özelliklerini ve bunların bize getirisine değinmek istiyorum. İllaki bir nevi temel özelliklere bakacağız fakat odak konu o olsun istemiyorum. Amacım JS üzerinden ilerleyenlere TypeScript(TS) kabaca nedir? ne değildir? onu gösterebilmek olacak.
Ek olarak React dünyasına yeni adım atanlar için şunu da söyleyeyim; TS tamamen React kısmı ile alakalı bir yapıdır. Bu yazıda yer alan kalıpları birebir ReactJS’de de, React Native’de de uygulayabilirsiniz. Ben örnekleri React Native üzerinden anlatacağım. Öncesinde de dediğim gibi; bu yazıyı TS’in size sunduklarını gösterip, tercihi size bırakarak yazmayı hedefliyorum. Çünkü olmazsa olmaz, zorunluluk gibi bir durum söz konusu değil. En nihayetinde yazılım dilleri bizler için sadece bir araç ve biz o istediğimiz sonuca, istediğimiz şekilde varabiliriz. O yüzden öncelikli olarak TS nedir ile başlamak istiyorum.
TypeScript
TypeScript; JavaScript dilinin üzerinde inşa edilmiş açık kaynaklı bir yazılım dilidir. Bir framework ya da kütüphane değildir. Kendisi tamamiyle JS’in belirli bir formatta yazılmasına olanak sağlayan, tip korumasını ön plana çıkartan, size compile değil development sürecinde sonuçları veren bir dildir. TS’de yazdığınız tüm kodlar günün sonunda JS’e dönüştürülür ve siz tamamiyle JS dilinde geliştirme yapmış olursunuz. TS size sadece JS’den evvel belirli kurallar ve kalıplar sunar. Aslında bu sebepten ötürü, yani tamamiyle JS tabanlı olduğu için kendisinden ayrı bir dil olarak bahsetmek pek fazla doğru gelmese de, yazım formatında JS’de olduğu kadar bir serbestlik sunmaması, JS’in o dinamikliğini engellemesinden ötürü özellikle yeni öğrenen kişilerin afallamasına yol açabiliyor.
Peki bu dinamikliğin olmamasından kastımız ne? Bir örnekle bakalım;
TYPE-Script
TS’in en belirgin özelliklerinden biri de “tip”ler. JS’de geliştirme yaparken tanımladığınız değişkenlerin tipleri dinamik olarak kontrol edilmezler. Oluşturduğunuz hiç bir değişkene sabit bir tip veremezsiniz ve JS sizi bu konuda uyarmaz.
İşte TS’in tipler olarak sunduğu özellik burada devreye giriyor. TS’de istediğiniz değişkene sabit bir tip/tipler belirleyebilirsiniz. Bir kere belirlediğiniz bir değişken tipi kod akışında hep sabit kalır ve o değişkene o belirlediğiniz tip dışında bir tipe sahip bir değer atamaya çalıştığınızda TS sizi uyarır. Siz kodu daha derlemeden.
Yani;
Şu şekilde bir kod bloğumuz olsun. İlk satırda tanımlanan productAmount adındaki değişkenin ilk değerini 20 olarak belirlemişiz. JS bu satırdan sonra o değişkenin tipini number olarak belirleyecektir. Bu belirleme özellikle IDE’lerdeki intellisense özelliği için kullanışlıdır. Tanımlanan değişkenin desteklediği built-in metotlar vb. işlemler için bu tip belirlemeleri gereklidir.
JS’in değişken için belirlediği tipi şu şekilde gözlemleyebiliyoruz. 20 değeri JS için number olduğundan artık değişken de number tipine sahip olmuş oldu.
Fakat; en son satırda bir bu değişkenimize bir string ataması yaptık. Tipi number olarak belirlenmiş bir değişkene farklı tipte bir değer gönderdik. JS bu noktada dinamik davrandığı için herhangi bir uyarı vs vermedi. JS için A tipindeki değişkene B tipinde bir değer de gönderilebilir. JS bu noktada kısıtlama yapmaz ve kod yapısı açısından da kabul edilebilir bir durum olduğu için bir sorun çıkmaz ve çalıştırsak da bir hata almayız. İşte bu noktada TS aynı şekilde davranmayacaktır.
Aynı kod bloğu TS formatında yazıldığında şekildeki çıktıyı verecektir. TS, ilk satırdan sonra productAmount değişkenine number tipini atayacak ve o değişkenin tipini korumak isteyecektir. En alt satırda o değişkene string formatında bir değer atamak istediğimizde de bize kızacaktır.
Şekilde görüldüğü gibi string tipini number tipine atayamayacağını söyleyecektir. Kod bloğu sağlıklı bir şekilde çalışır mı? Evet çalışır fakat TS bizi tip korumaya zorlayacaktır. Kod çalışsa da çalışmasa da bize kızacaktır. İşte tip koruma özelliği TS yapısında bu şekilde işlemekte.
Derleme Öncesi Uyarı
TS compile değil development safhasında bize uyarı verir demiştik. Bu nasıl işliyor derseniz basit bir örnekle gösterelim hemen;
Kod bloğumuzda number tipindeki bir değişkende string bir metot (desteklenmeyen herhangi bir built-in metot gibi düşünebilirsiniz) tetiklenmeye çalışılmış. JS bu noktada bize herhangi bir uyarı vermiyor ve biz sonucu derlediğimiz zaman, yani compile zamanında görebiliyoruz. Yani;
Şeklinde bir çıktı almaktayız.
Fakat TS o şekilde davranmaz;
Aynı kod bloğunu TS üzerinde yazdığımızda TS direkt olarak bize uyarı verecektir. Mevcut değişken tipinde böyle bir metodun bulunmadığını bize söylemekte ve biz bu uyarı kod daha derlenmeden almaktayız. Kodu derlediğinizde de çıktı yine JS’deki gibi olacaktır zaten.
Bu özelliğin geliştirme sürecinde inanılmaz bir desteği mevcut. Büyük çaplı projelerde ölçek büyüdükçe compile edip gözden kaçan ufak bir tip hatasıyla vakit kaybetmek çok can sıkıcı olabiliyor. İşte TS size daha derleme olmadan bu şekilde çıktıları veriyor ve size zaman kazandırıyor.
Mantıksal Model İmkanı
JS ile proje geliştirirken elinizdeki yapılara bir tip, bir model gibi yaklaşamayabiliyorsunuz. Yani bir Person tipinde nesneniz var ise bunu belirli bir model kalıbına koyamıyorsunuz. Elbette class’lar ile bir nebze çözülebiliyor fakat TS’de bu olay daha dinamik ve kolay.
TS’de herhangi bir objeye belirli bir özellik ve tanım kazandırmak istiyorsanız interface(arayüz) ya da type(tip) verebiliyorsunuz. (Yazıya Türkçeleri ile devam edeceğim, dil katliamı olsun istemiyorum 😅) Bunlar; ilgili objeye mantıksal olarak yaklaşmanıza ve kullanımını belirlemenize olanak sağlar. Daha öncesinde nesne odaklı bir dil ile çalıştıysanız bunlara zaten haşır neşir olmuşsunuzdur.
Örneğin bir Person arayüzüne ve onun kullanımına bakacak olursak;
Üzerinde çalıştığımız yapılara bir obje modeli belirlemek onlara mantıksal olarak yaklaşmak demektir. Yani bir “kişi” modeli üzerinde duruyorsak o yapı bizim için bir anlam ifade ediyor demektir. O kişinin sahip olduğu özellikler (isim, yaş, meslek), yapabildiği şeyler (yürüme, koşma, konuşmak) gibi özellikleri biz o kişinin arayüzünü belirlerken tanımlayabiliyoruz.
Aynı bakış açısını React bileşenleri için de uygulayabiliriz elbette.
Şekilde görüldüğü üzere öncelikle bir Order tipinde nesnemiz belirlenmiş ve o tip OrderCard tipinde kullanılmış. Çünkü Order da bizim için ayrı bir amaç taşıyan nesne konumunda artık. Bu sayede bileşenimize tip belirlemesi yapabiliyor ve o tip üzerinden tanımlama yapabiliyoruz.
Burada özellikle dikkat etmemiz gereken şey arayüz tanımlayabiliyor olmak vs. değil, oluşturduğumuz her bileşene mantıksal bir anlam yükleyebilmek. React ile geliştirme yaparken oluşturduğumuz her bir bileşen belirli bir amaca hizmet etmeli. Her bir iş parçacığı sizin için birer bileşen olmalı ki bu geliştirme disiplinini kendi dökümanlarında çok güzel bir şekilde açıklamışlar. Siz x y z işini yapmasını hedeflediğiniz bir A bileşeni tasarlıyorsanız o bileşen k l m işlerini yapmamalı. İşte bu noktada TS size arayüz tanımlama imkanı sunarken aslında mantıksal olarak da düşünmenize olanak sağlıyor. OrderCard bileşenini tasarlarken o bileşenin taşıyacağı özellikleri onun arayüzünü yazarken belirleyebiliyorsunuz. Yani tamamiyle bir mantık çerçevesinden bakabiliyorsunuz olaya. TS size bu imkanı sunuyor.
Bu konu gerçekten önemli bir başlık çünkü tasarladığınız projeyi mantıksal olarak parçalara ayıramıyorsanız yanlış bir yol izliyorsunuz demektir. Kod okuması zor, birbiriyle çok iç içe (coupling, cohesion hak getire) modüllerin olduğu bir proje kötü bir projedir. Bunun önüne geçebilmek için TS’in sunduğu bu yapılar bize yardımcı oluyor. Ha, illa TS mi lazım bunun için? Elbette hayır. JS ya da başka bir dil ile de bu şekilde kontrollü bir sistem kurulabilir. Ama TS bize böyle bir kolaylık sağlıyorsa, faydalanabiliriz 🍻
Interface & Type Gücü
Diyelim ki kendimiz bir buton bileşeni tasarlamak istiyoruz. Normal bir buton gibi davransın, fakat tasarımını vs. biz kendimiz belirleyelim istiyoruz. Bunu JS formatında şu şekilde yapabiliriz pek tabii;
Fakat bu bileşen saf bir buton gibi çalışmayacaktır. Neden diyecek olursak bu bileşende buton özelliğini veren ana yapı TouchableOpacity bileşeni ve biz onun sadece onPress propuna bir değer gönderiyoruz. Peki ya tüm özelliklerine erişmek istesek? Bunu da tip korumalı bir şekilde yapmak istersek?
İşte o noktada TS’in kalıtım yapısı devreye giriyor ve bizi kurtarıyor. Aynı bileşeni TS ile yazmak istersek de;
Burada dikkatimizi çeken şey Button bileşeni için arayüz tanımlarken TouchableOpacityProps arayüzünü kullanmamız. TouchableOpacityProps’u extend ettiğimizde (kalıttığımızda) ButtonProps arayüzümüze TouchableOpacity’inin bütün özelliklerini aktarmış oluyoruz. Arayüzümüzü kullanırken rest adındaki (istediğiniz bir ismi kullanabilirsiniz, title ve loading dışındaki tüm propları -doğal olarak kalıttığımız propun değerlerini- temsil eder) propumuzu TouchableOpacity bileşenimize aktardığımızda da Button bileşenimizi tamamlamış oluyoruz.
Artık butonumuz saf bir TouchableOpacity bileşeni gibi davranacak. Çünkü onun sahip olduğu özellikleri taşıyor ve onun gibi kullanılabiliyor.
Görüldüğü üzere Button bileşenimizde onFocus, onBlur, onLongPress gibi bir çok metot bulunmakta. Bunların hiç birini biz yazmadık çünkü hepsini arayüzümüzde TouchableOpacityProps arayüzünden kalıtıp TouchableOpacity bileşenimize aktardık. Artık bileşenimiz gerçek bir buton gibi davranmakta.
TS sayesinde.
Örnek Bir TypeScript Nimeti: Literal Types
TS’in tonlarca özelliği var. Tip kalıtımalardan Utility Types’lara, enums’lardan Decorator’lara bir çok özelliğe sahip bir dil. Ben burada ufak bir örnek vermek istiyorum.
Mesela az önce oluşturduğumuz Button bileşenimize bir tema özelliği eklemek isteyelim. Örneğin primary, outline ve warning tema seçenekleri olsun ve varsayılan olarak da primary temasında olsun. Bunu nasıl yaparız dersek;
Örneğin Button bileşenimizin stillerini belirlediğimiz dosyamız şu şekilde olsun;
Üç farklı tema için üç farklı stil belirlemesi yaptık. Her bir stilin kendi içinde container ve title tasarımları da mevcut. Ben iyice kod kalabalığı olmaması için içlerini doldurmadım ama ilgili temalara göre farklı tasarımlar olduğunu varsayabilirsiniz.
NOT: Üç temada da aynı durumdaki tasarım kodlarının tekrarını önlemek için BaseStyle adında bir stil belirlenip o üç tasarımda da dağıtılabilir. Örneğin warning tasarımının title bileşeni için {…baseStyle.title, color: “red”} gibi. Ana temayı kullan, color’ı overwrite et mantığı.
Button bileşenimizde ise theme adında yeni bir prop’umuz olsun.
Arayüz tanımlamasında theme adlı prop’umuzun alabileceği değerleri şekilde görüldüğü üzere belirttik. Bu gördüğünüz tanımlamada theme; yalnızca primary, outline ve warning değerlerini alabilir. Yani tamamiyle bu üç değeri kabul eden bir property konumunda. Ek olarak yanda bulunan soru işareti ise optional bir değer olduğunu belirtiyor. Yani bu bileşeni kullanan kişi eğer istemezse theme prop’unu tanımlamayabilir, undefined olabilir. Bu sayede theme değerinin kullanımını ya bu üç değer ya da hiç ile kısıtlamış olduk. Tam bir seçenek konumunda.
E peki bunu stile nasıl aktaracağız?
Şöyle;
Dikkat ettiyseniz TouchableOpacity ve Text bileşenlerimizin style prop’una theme değerimizi object array notation formatında ilettik. Tahmin edeceğiniz üzere styles adındaki değer bizim stil dosyamızdaki ana stil objesi ve hatırlayacak olursanız baştan sonra bir object return ediliyordu. E ben zaten objelere array notation (user[“age”]) ya da dot notation (user.age) formatında erişebiliyordum? O zaman theme değerimi styles nesnemin içine gönderirsem tam da bu bileşeni kullanacak kişinin istediği stil değerine erişim sağlamış olurum.
Ek olarak varsayılan değeri de prop’ları parçalarken primary atamasıyla yapmış olduk. Eğer theme tanımlanmaz ise varsayılan olarak primary stili atanmış olacak.
Tertemiz bir buton bileşenimiz oldu 😚🤌
tsconfig
Son olarak da tsconfig.json’dan bahsedelim.
TS ile bir proje geliştirirken TS’in sizin projenizi nasıl kontrol edeceğini belirleyebiliyorsunuz. Sonuçta TS development-time çalışan bir yapıya sahip ve size dinamik olarak çıktılar veriyor. Bu ve bunun gibi bir çok özelliği tsconfig üzerinden tanımlayabilirsiniz.
React Native’de TS template kullanarak bir proje oluşturduğunuzda ortaya çıkan tsconfig dosyası şu şekilde;
Burada projenizin hangi EcmaScript’i kullanacağından tutun da hangi type-checking kontrollerini yapacağına kadar siz yönetebiliyorsunuz TS’i. Örneğin Strict Type-Checking Option başlığı altında kalan kısımlar kullanılmayan local variable’larda uyar, implicit return’leri kabul etme gibi kuralları tanımlayabiliyorsunuz. Gerçi bu işleri eslint daha genel bir çatıda hallediyor ama TS’in de böyle nimetleri var ✌️
Meraklısına: typescript/tsconfig
Sonuç
TypeScript’imiz işte böyle. Daha bahsedemediğim tonla özelliği mevcut fakat umuyorum ki aklınızda bir şeyler şekillenebilmiştir. Eğer ilginizi çektiyse mutlaka öğrenmeye başlayın derim.
Şahsi görüşüm; büyük küçük demeden projelerde TS kullanmak her daim daha profesyonel çalışmanızı sağlayacaktır. Özellikle takım ve ölçek büyüdükçe çok daha kontrollü ve dikkatli kod yazmanızı sağlıyor TS. Hele ki test sürecinde hayat kurtarıcı oluyor.
Benden bu seferlik bu kadar dostlar, umarım güzel ve faydalı bir yazı olmuştur. Sorunuz vs. olursa yorum atabilir ya da sosyal medyadan ulaşabilirsiniz.
Kendinize çok iyi bakın, sağlıkla kalın 👊