Yukarıda paylaştığınız TypeScript eğitim müfredatını detaylıca analiz ettim. İstediğiniz kurallar doğrultusunda süreleri, önizleme yazılarını ve ders sayılarını temizledim. İçeriği tam olarak anlamanız için özet geçmeden, bolca kod örneği ve pratik uygulamalar ekleyerek büyük bir rehber hazırladım.
Dosya çok kapsamlı ve uzun olduğu için eğitim kalitesini düşürmemek adına bu ilk bölümde "Öğrenecekleriniz" kısmının detaylı açıklamasını, "Kurulumlar" ve "Data Türleri (Temel ve İleri Seviye Tipler)" konularını eksiksiz ve uygulamalı olarak ele alıyorum. Kalan konuları (Fonksiyonlar, Nesne Yönelimli Programlama, Generic yapılar vb.) bir sonraki adımda işleyeceğiz.
Bu paragraf, kursun size kazandıracağı temel yetenekleri ve TypeScript'in neden var olduğunu anlatır. Maddeler halinde ne anlama geldiklerini inceleyelim:
Güçlü Tip Sistemi ile Güvenli Kod Yazmak: JavaScript esnek bir dildir fakat bu esneklik hata yapmayı kolaylaştırır. Örneğin bir fonksiyona sayı yerine yanlışlıkla metin (string) gönderirseniz JavaScript bunu hemen fark etmez ve programınız çalışırken çöker. TypeScript ise kod daha çalışmadan, siz yazarken bu hatayı yakalar ve güvenli bir zemin sunar.
OOP (Nesne Yönelimli Programlama) Kavramları: Büyük projelerde kodların birbirine karışmaması istenir. Sınıflar (Classes), Miras alma (Inheritance) ve Çok Biçimlilik (Polymorphism) sayesinde tıpkı Lego parçaları gibi düzenli, bakımı kolay ve kurumsal mimariye uygun yapılar kurabilirsiniz.
Modülerlik
ve Yeniden Kullanılabilirlik: Kodları Interface
(Arayüz), Namespace
(İsim uzayı) ve Module
yapılarına bölerek, bir kez yazdığınız bir özelliği
projenin onlarca farklı yerinde hata yapma riski olmadan tekrar
tekrar kullanabilirsiniz.
Asenkron Programlama ve API Entegrasyonu: İnternetteki bir sunucudan veri çekmek (örneğin hava durumu veya kullanıcı listesi) zaman alır. Kodun bu sürede donmaması için asenkron (arka planda çalışan) yapılar kurulmalıdır. TypeScript ile gelen verilerin tipini belirleyerek API işlemlerini hatasız yönetirsiniz.
Proje Geliştirme ve Test: Yazdığınız kodların doğru çalışıp çalışmadığını otomatize testlerle denetlemeyi ve gerçek bir projeyi baştan sona TypeScript ile ayağa kaldırmayı öğrenirsiniz.
TypeScript kodları doğrudan tarayıcılar (Chrome, Edge vb.) veya Node.js tarafından çalıştırılamaz. Önce JavaScript'e dönüştürülmesi gerekir.
Bilgisayarınızda Node.js kurulu
olmalıdır. Ardından terminale şu komutu yazarak TypeScript
derleyicisini (tsc)
küresel olarak kurarsınız:
npm install -g typescriptKod yazmak için en verimli ortam Visual Studio Code (VS Code) editörüdür. Geliştirme sürecini hızlandırmak için şu eklentileri kurmanız önerilir:
IntelliSense: Kod tamamlama ve hataları anında gösterme.
Material Icon Theme: Dosya türlerini görsel simgelerle ayırma.
Boş bir klasör oluşturun ve
içine app.ts
isimli bir dosya açıp şu kodu yazın:
letmesaj:string="Merhaba TypeScript!";console.log(mesaj);
Terminale tsc
app.ts
komutunu verdiğinizde, TypeScript bu dosyayı okur ve tarayıcıların
anlayacağı app.js
dosyasını otomatik olarak üretir.
TypeScript'in en güçlü tarafı değişkenlerin hangi türde veri tutacağını kesin olarak belirleyebilmemizdir.
En temel veri yapıları sayısal, metinsel ve mantıksal değerlerdir.
letyas:number=26;letisim:string="Ahmet";letogrenciMi:boolean=true;// Hata Durumu (TypeScript burada hemen kırmızı çizgi çeker):// yas = "Yirmi Altı"; // HATA: 'string' türü 'number' türüne atanamaz!
Siz açıkça tip belirtmeseniz de TypeScript akıllıdır ve değişkene ilk verdiğiniz değerden onun tipini tahmin eder. Buna Tip Çıkarımı denir.
letsehir="Isparta";// TypeScript bunun 'string' olduğunu otomatik anlar.// sehir = 32; // HATA verir!// Açıkça belirtmek istersek (Tip Ataması):letplaka:number=32;
Bir nesnenin içindeki alt özelliklerin de hangi tipte olacağını tek tek belirleyebiliriz.
letkullanici: {id:number;isim:string;aktif:boolean};kullanici = {id:1,isim:"Can",aktif:true};// Eksik veya yanlış tip girilirse TypeScript uyarır:// kullanici = { id: 2, isim: "Ece" }; // HATA: 'aktif' özelliği eksik!
Bir dizinin sadece belirli bir tipte veri kabul etmesini sağlarız.
lethobiler:string[] = ["Yüzme","Kitap Okuma","Yazılım"];// Güvenli Kullanım: Dizideki elemanların string olduğunu bildiğimiz için// string fonksiyonlarını (örneğin toUpperCase) güvenle kullanabiliriz.hobiler.forEach((hobi) => {console.log(hobi.toUpperCase());// Çıktı: YÜZME, KİTAP OKUMA...});
TypeScript, standart JavaScript'te olmayan veya JavaScript'i daha güvenli hale getiren özel gelişmiş tipler sunar.
Eleman sayısı ve hangi indekste hangi tipin olacağı kesin olarak belli olan özel dizilerdir.
letapiCevabi: [number,string] = [200,"Başarılı"];// apiCevabi = ["Hata", 404]; // HATA: Sıralama yanlış! Sayı önce gelmeli.
Kod içinde sürekli kullanılan sabit kelime gruplarını (örneğin roller, günler, yönler) merkezi bir yapıda toplar.
enumRol{Admin,// Arka planda 0 değerini alırEditor,// Arka planda 1 değerini alırUye// Arka planda 2 değerini alır}letmevcutKullaniciRolü: Rol = Rol.Admin;if(mevcutKullaniciRolü === Rol.Admin) {console.log("Yönetim paneline erişim izni verildi.");}
any
türü, değişkenin her türlü değeri almasına izin verir. Ancak
projenin tip güvenliğini tamamen yok ettiği için kullanılması
önerilmez. Yerine daha güvenli olan unknown
tercih edilir.
letrastgeleVeri:any=10;rastgeleVeri ="Şimdi metin oldum";// Hata vermez, serbesttir.letbelirsizVeri:unknown="Merhaba";// let metinUzunlugu: number = belirsizVeri.length; // HATA: Tipi garanti etmeden kullanamazsın!// Güvenli kontrol (Type Guard):if(typeofbelirsizVeri ==="string") {console.log(belirsizVeri.length);// Burada artık güvenli!}
Union
Types: Bir değişkenin birden fazla tipe sahip olabilmesini
sağlar (|
işareti ile kullanılır).
Literal Types: Değişkenin sadece bizim belirlediğimiz spesifik kelimeleri veya sayıları alabilmesini sağlar.
// Sadece bu üç metinden biri girilebilir (Literal + Union):letkargoDurumu:"hazirlaniyor"|"yolda"|"teslim-edildi";kargoDurumu ="yolda";// Geçerli// kargoDurumu = "iptal-edildi"; // HATA: Bu kelime izin verilen listede yok!// Sayı veya Metin kabul eden Union örneği:letKimlikNo:string|number;KimlikNo =123456;KimlikNo ="TR123456";
Void: Genelde bir değer döndürmeyen (sadece ekrana yazı yazan, işlem yapan) fonksiyonlarda kullanılır.
Never: Hiçbir zaman bitmeyecek (sonsuz döngü) veya her zaman hata fırlatacak fonksiyonlar için kullanılır.
functionlogMesaj(mesaj:string):void{console.log("Sistem Mesajı: "+ mesaj);// return 5; // HATA: void fonksiyon bir şey döndüremez!}functionhataFirlat(mesaj:string):never{thrownewError(mesaj);// Kod burada kesilir, asla bir değer üretemez.}
Harika! Eğitim rehberimize kaldığımız yerden, özet geçmeden ve bolca kod örneğiyle devam ediyoruz. Müfredattaki sıraya sadık kalarak bu bölümde Functions (Fonksiyonlar) ve Control Flow Statements (Kontrol Akış Yapıları ve Döngüler) konularını tüm detaylarıyla, satır satır işliyoruz.
TypeScript'te fonksiyonlar, hem parametrelerin (girdi) hem de fonksiyonun üreteceği sonucun (çıktı) tip güvenliğini korumak için tasarlanmıştır.
JavaScript esnekliğinde gelen,
function
kelimesi yerine =>
(ok) işaretini kullanan, daha kısa ve okunabilir yazım tarzıdır.
Özellikle dizilerle çalışırken (callback işlemleri) hayat
kurtarır.
// İki sayıyı çarpan basit bir arrow fonksiyonuconstkdvHesapla= (fiyat:number,oran:number):number=> {returnfiyat * oran;};// Tek satırlık yazım (Süslü parantez ve return yazılmazsa otomatik döner)constikiKatiniAl= (sayi:number):number=> sayi *2;// Dizilerde callback (geri çağırım) olarak kullanımıconsturunFiyatlari:number[] =;constucuzUrunler= urunFiyatlari.filter(fiyat => fiyat <400);// Çıktı: [100, 250]
Optional
(?):
Bir parametrenin gönderilmesinin zorunlu olmadığını belirtir.
Eğer gönderilmezse değeri undefined
olur. Opsiyonel parametreler her zaman en sona yazılmalıdır.
Default (=):
Eğer kullanıcı o parametreyi boş bırakırsa, arka planda
otomatik olarak kullanılacak sabit bir değer tanımlar.
// 'unvan' opsiyoneldir, 'rol' ise varsayılan olarak "Üye" tanımlanmıştırfunctionkullaniciSelamla(isim:string,unvan?:string,rol:string="Üye"):string{if(unvan) {return`Merhaba ${unvan} ${isim}, Rolünüz: ${rol}`;}return`Merhaba ${isim}, Rolünüz: ${rol}`;}console.log(kullaniciSelamla("Ahmet"));// Çıktı: "Merhaba Ahmet, Rolünüz: Üye"console.log(kullaniciSelamla("Ece","Dr.","Yönetici"));// Çıktı: "Merhaba Dr. Ece, Rolünüz: Yönetici"
Bir fonksiyonun, başka bir fonksiyona parametre (girdi) olarak gönderilmesidir. TypeScript'te bu girdinin de yapısını (hangi parametreleri alıp ne döneceğini) tiplerle koruma altına alırız.
// callback fonksiyonunun yapısı: iki sayı almalı ve sayı döndürmelifunctionislemMerkezi(a:number,b:number,islem: (x:number,y:number) =>number):void{constsonuc= islem(a, b);console.log(`İşlem Sonucu: ${sonuc}`);}consttoplama= (s1:number,s2:number):number=> s1 + s2;constcarpma= (s1:number,s2:number):number=> s1 * s2;islemMerkezi(5,3, toplama);// Çıktı: İşlem Sonucu: 8islemMerkezi(5,3, carpma);// Çıktı: İşlem Sonucu: 15
Aynı isimde fakat farklı parametre tipleriyle veya farklı sayıda parametreyle çalışabilen fonksiyonlar tanımlamamızı sağlar. Önce imza (tanım) satırları yazılır, en son gövde (çalışacak kod) yazılır.
// 1. İmza: Sadece tek bir metin alırsafunctionmesajYaz(metin:string):void;// 2. İmza: Bir metin dizisi alırsafunctionmesajYaz(metinler:string[]):void;// 3. Gerçek Gövde (Implementation): Yukarıdaki tüm imzaları karşılamalıdırfunctionmesajYaz(veri:any):void{if(typeofveri ==="string") {console.log(`Tek Mesaj: ${veri}`);}elseif(Array.isArray(veri)) {console.log(`Çoklu Mesajlar: ${veri.join(" - ")}`);}}mesajYaz("Merhaba!");// GeçerlimesajYaz(["Hata 1","Hata 2","Hata 3"]);// Geçerli
Bir fonksiyona kaç tane parametre
geleceği belli olmadığında, gelen tüm değerleri tek bir dizi
altında toplamak için kullanılır. Başında ...
(üç nokta) işareti taşır.
functionhepsiniTopla(ilkSayi:number, ...kalanSayilar:number[]):number{lettoplam= ilkSayi;kalanSayilar.forEach(sayi => toplam += sayi);returntoplam;}console.log(hepsiniTopla(10,20,30,40,50));// Çıktı: 150
Kodun hangi şartlarda nereye sapacağını (koşullar) veya bir işlemi kaç kez tekrar edeceğini (döngüler) belirleyen yapılardır.
If - Else: Mantıksal bir şart doğruysa bir bloğu, yanlışsa diğer bloğu çalıştırır.
Ternary (Üçlü Operatör):
Kısa ve tek satırlık if-else
yazımıdır. şart
? doğruysa : yanlışsa mantığıyla çalışır.
letbakiye:number=500;leturunFiyati:number=600;// Klasik If - Elseif(bakiye >= urunFiyati) {console.log("Satın alabilirsiniz.");}else{console.log("Bakiye yetersiz.");}// Ternary Operatörü ile kısa yazımletsonucMesaji:string= bakiye >= urunFiyati ?"Onaylandı":"Reddedildi";
Bir değişkenin alabileceği çok
fazla sabit değer olduğunda, upuzun if-else
blokları yazmak yerine kodun okunabilirliğini artırmak için
tercih edilir.
enumIsik{Kirmizi,Sari,Yesil}letmevcutIsik: Isik = Isik.Sari;switch(mevcutIsik) {caseIsik.Kirmizi:console.log("Dur!");break;caseIsik.Sari:console.log("Hazırlan!");break;caseIsik.Yesil:console.log("Geç!");break;default:console.log("Bilinmeyen lamba durumu.");}
For Loop: Başlangıç, bitiş ve artış miktarı kesin belli olan tekrarlı işlemlerde kullanılır.
While Loop: Belirli bir şart doğru (true) olduğu sürece dönmeye devam eder. Şartın bir noktada bozulması gerekir, aksi halde sonsuz döngü oluşur.
// For Döngüsü ile 1'den 3'e kadar saymafor(leti:number=1; i <=3; i++) {console.log(`For Sayacı: ${i}`);}// While Döngüsü ile geri sayımletsayac:number=3;while(sayac >0) {console.log(`While Sayacı: ${sayac}`);sayac--;// Şartı bozmak için sayacı azaltıyoruz}
Dizilerin (Arrays) içinde gezinmek için üretilmiş en modern ve güvenli metottur. Dizideki her bir eleman için sırayla bir fonksiyon çalıştırır. Döngü sırasında elemanın kendisine, o elemanın sırasına (index) ve dizinin orijinal haline erişebiliriz.
// Nesnelerden oluşan bir dizi tanımlayalımletsepet: {id:number;urunAdi:string;adet:number}[] = [{id:1,urunAdi:"Laptop",adet:1},{id:2,urunAdi:"Fare (Mouse)",adet:2},{id:3,urunAdi:"Klavye",adet:1}];// forEach ile sepeti ekrana güvenle basalımsepet.forEach((urun,siraNo,tumSepet) => {console.log(`${siraNo +1}. Sıradaki Ürün: ${urun.urunAdi} - Adet: ${urun.adet}`);});/*Çıktı:1. Sıradaki Ürün: Laptop - Adet: 12. Sıradaki Ürün: Fare (Mouse) - Adet: 23. Sıradaki Ürün: Klavye - Adet: 1*/
Harika! Yolculuğumuza TypeScript'in en güçlü ve kurumsal projelerde en çok kullanılan kalbiyle devam ediyoruz. Bu bölümde müfredatta yer alan TypeScript'te Interface Kullanımı ve TypeScript'te Class Kullanımı konularını, satır satır mantığını anlayarak ve bolca pratik kod mimarisi kurarak işliyoruz.
Interface
(Arayüz), bir nesnenin (object) veya sınıfın (class) hangi
özelliklere ve fonksiyonlara sahip olması gerektiğini belirleyen
bir sözleşmedir.
İçinde gerçek kodlar yazılmaz; sadece kurallar (şablonlar)
tanımlanır.
Bir nesnenin uyması gereken zorunlu alanları belirleriz.
// Sözleşmeyi imzalıyoruzinterfaceCalisan{id:number;isim:string;departman:string;}// Nesnemizi oluştururken bu sözleşmeye uymak zorundayızconstyeniCalisan: Calisan = {id:101,isim:"Aslı Yılmaz",departman:"Yazılım Geliştirme"};// Hata Durumu:// const eksikCalisan: Calisan = { id: 102, isim: "Mert" }; // HATA: 'departman' eksik!
Interface'ler sadece nesne özelliklerini değil, bir fonksiyonun alacağı parametreleri ve döneceği veri tipini de kurala bağlayabilir.
interfaceMatematikIslemi{(sayi1:number,sayi2:number):number;// İki sayı alıp bir sayı dönmeli}// Sözleşmeye uygun bir fonksiyon yazıyoruzconstbolmeIslemi: MatematikIslemi = (x,y) => {if(y ===0)return0;returnx / y;};
?)
Özellikler
Bazı verilerin girilmesi zorunlu
olmayabilir. Tıpkı değişkenlerde olduğu gibi ?
işareti koyarak bir alanı isteğe bağlı yapabiliriz.
interfaceUrun{id:number;ad:string;fiyat:number;renk?:string;// Bu alan girilmese de TypeScript hata vermez}consturun1: Urun = {id:1,ad:"Klavye",fiyat:450};// Geçerliconsturun2: Urun = {id:2,ad:"Telefon",fiyat:15000,renk:"Siyah"};// Geçerli
Readonly: Nesne ilk kez oluşturulduktan sonra içindeki bir özelliğin değerinin bir daha asla değiştirilememesini sağlar.
Readonly Array: Bir dizinin
elemanlarının değiştirilmesini, diziye yeni eleman eklenmesini
(push)
veya silinmesini engeller.
interfaceAyar{readonlyapiEndpoint:string;// Değiştirilemez!versiyon:string;}constsistemAyari: Ayar = {apiEndpoint:"https://sirket.com",versiyon:"1.0.4"};sistemAyari.versiyon ="1.0.5";// Geçerli// sistemAyari.apiEndpoint = "https://hacker.com"; // HATA: Salt okunur alan değiştirilemez!// Readonly Array ÖrneğiletkorumaliSayilar:readonlynumber[] =;// korumaliSayilar.push(40); // HATA: 'push' metodu bu dizi için kullanılamaz!
Bir interface, başka bir interface'in kurallarını miras alarak kendi üzerine ekleyebilir. Böylece kod tekrarı yapmamış oluruz.
interfaceStandartKullanici{id:number;:string;}// Admin, standart kullanıcının tüm özelliklerine ek olarak yetki seviyesine sahip olurinterfaceAdminKullaniciextendsStandartKullanici {yetkiSeviyesi:number;sistemLoglariniOku():void;}constadmin: AdminKullanici = {id:1,:"admin@sirket.com",yetkiSeviyesi:5,sistemLoglariniOku() {console.log("Loglar okunuyor...");}};
Sınıflar, gerçek dünyadaki nesneleri kod dünyasında modellemek için kullandığımız kalıplardır. İçlerinde hem veri (özellikler) hem de bu verileri işleyecek fonksiyonlar (metotlar) barındırırlar.
Sınıftan new
anahtar kelimesi ile yeni bir kopya (nesne) üretildiğinde otomatik
olarak ilk çalışan fonksiyona constructor
denir. Sınıfın özelliklerine ilk değerlerini vermek için
kullanılır.
classAraba{marka:string;// Sınıf özelliği (Property)yil:number;// Kurucu Fonksiyonconstructor(markaAdi:string,uretimYili:number) {this.marka = markaAdi;this.yil = uretimYili;}bilgiVer():void{console.log(`Bu araç bir ${this.marka} ve ${this.yil} modeldir.`);}}// Sınıfı kullanmaconstbenimArabam=newAraba("Toyota",2022);benimArabam.bilgiVer();// Çıktı: Bu araç bir Toyota ve 2022 modeldir.
Sınıfın içindeki verilere dışarıdan kimlerin erişebileceğini belirleyen güvenlik duvarlarıdır.
Public (Genel): Her yerden (sınıf içinden, dışından, miras alan alt sınıflardan) erişilebilir. Varsayılan olarak her şey public'tir.
Private (Özel): Sadece ve sadece tanımlandığı sınıfın içinden erişilebilir. Dışarıdan veya alt sınıflardan okunamaz, değiştirilemez.
Protected (Korumalı): Sınıfın içinden ve o sınıfı miras alan alt sınıflardan erişilebilir. Daire dışındaki yabancılara (doğrudan dış kodlara) kapalıdır.
classBankaHesabi{publicmusteriAdi:string;// Herkes görebilirprivatebakiye:number;// Sadece bu sınıf değiştirebilirprotectedsubeKodu:string;// Bu sınıf ve çocukları görebilirconstructor(isim:string,ilkBakiye:number,sube:string) {this.musteriAdi = isim;this.bakiye = ilkBakiye;this.subeKodu = sube;}// Bakiyeyi güvenli bir şekilde dışarıya açmak için metot yazıyoruzpublicparaYatir(miktar:number):void{if(miktar >0) {this.bakiye += miktar;console.log(`${miktar} TL yatırıldı. Yeni bakiye: ${this.bakiye}`);}}}consthesap=newBankaHesabi("Caner",1000,"3201");hesap.musteriAdi ="Caner Tekin";// Geçerli (Public)// hesap.bakiye = 500000; // HATA: bakiye 'private' olduğu için dışarıdan doğrudan değiştirilemez!hesap.paraYatir(500);// Güvenli geçiş yöntemi
this
Anahtar Kelimesi
Sınıfın içindeki fonksiyonlara
metot denir.
Metotların içinde, sınıfın kendi özelliklerine (marka,
bakiye
vb.) erişebilmek için mutlaka this
kelimesini kullanmak zorundayız. this,
"şu an üzerinde işlem yaptığın nesne" anlamına
gelir.
classSayac{deger:number=0;artir():void{this.deger++;// this olmadan 'deger' değişkenini bulamaz}mevcutDeger():void{console.log(`Güncel Sayı: ${this.deger}`);}}constislem =newSayac();islem.artir();islem.mevcutDeger();// Çıktı: Güncel Sayı: 1
Harika! Müfredatımızdaki bir sonraki kritik aşamaya geçiyoruz. Bu bölümde, Nesne Yönelimli Programlamanın (OOP) en güçlü mimari yapı taşları olan Inheritance (Miras Alma), Soyut Sınıflar (Abstract Classes) ve Polymorphism (Çok Biçimlilik) konularını satır satır kodlayarak işliyoruz.
Büyük projelerde ortak özelliklere sahip sınıfları tekrar tekrar yazmak yerine, bir ana sınıf oluşturup diğer sınıfları ondan türetiriz.
super
Anahtar Kelimesi
Bir sınıfın (alt sınıf), başka
bir sınıfın (ana sınıf) tüm public
ve protected
özelliklerini ve metotlarını devralmasına Miras
denir. Alt sınıfın kurucu fonksiyonu (constructor)
içinde, ana sınıfın kurucusunu tetiklemek için super()
kelimesini kullanmak zorundayız.
// Ana Sınıf (Base Class)classPersonel{ad:string;maas:number;constructor(ad:string,maas:number) {this.ad = ad;this.maas = maas;}calis():void{console.log(`${this.ad} şu an standart görevlerini yapıyor.`);}}// Alt Sınıf (Derived Class) - Personel sınıfından miras alıyorclassYoneticisextendsPersonel {ekipBoyutu:number;constructor(ad:string,maas:number,ekipBoyutu:number) {super(ad, maas);// Ana sınıfın constructor'ını çağırıyoruzthis.ekipBoyutu = ekipBoyutu;// Kendine ait yeni özellik}// Metot Ezme (Method Overriding): Ana sınıftaki metodu yöneticiye özel değiştirmekcalis():void{console.log(`${this.ad}, ${this.ekipBoyutu} kişilik ekibini yönetiyor.`);}toplantiYap():void{console.log(`${this.ad} strateji toplantısı başlattı.`);}}constmudur=newYoneticis("Zeynep Kaya",45000,12);mudur.calis();// Çıktı: Zeynep Kaya, 12 kişilik ekibini yönetiyor. (Ezilmiş metot çalıştı)mudur.toplantiYap();// Çıktı: Zeynep Kaya strateji toplantısı başlattı.
abstract
(soyut) sınıflar, doğrudan new
kelimesi ile nesnesi üretilemeyen
rehber sınıflardır. Amacı, kendisinden türeyecek alt sınıflara
kesin kurallar koymaktır.
Abstract Method: Gövdesi (kod bloğu) olmayan, alt sınıfların mutlaka kendi içinde yazmak zorunda olduğu metot şablonudur.
// Soyut SınıfabstractclassVeritabani{// Normal metot (Ortak özellik)baglan():void{console.log("Veritabanı bağlantısı başarıyla kuruldu.");}// Soyut Metot (Alt sınıf kendine göre kodlamak zorunda)abstractveriKaydet(veri:string):void;}// Alt Sınıf 1classMySqlVeritabaniextendsVeritabani {veriKaydet(veri:string):void{console.log(`'${veri}' verisi MySQL kurallarına göre kaydedildi.`);}}// Alt Sınıf 2classMongoVeritabaniextendsVeritabani {veriKaydet(veri:string):void{console.log(`'${veri}' verisi NoSQL JSON formatında MongoDB'ye kaydedildi.`);}}// const db = new Veritabani(); // HATA: Soyut sınıflar doğrudan üretilemez!constmysql=newMySqlVeritabani();mysql.baglan();// Çıktı: Veritabanı bağlantısı başarıyla kuruldu.mysql.veriKaydet("Kullanıcı Listesi");// Çıktı: 'Kullanıcı Listesi' verisi MySQL kurallarına göre kaydedildi.
Aynı ana sınıftan türeyen farklı alt sınıfların, aynı isimdeki bir metodu kendilerine has şekillerde çalıştırma yeteneğidir. Kodun tek bir kalıptan beslenip, çalışma anında farklı şekillere (biçimlere) girmesini sağlar.
classBildirim{gonder(mesaj:string):void{console.log(`Genel Bildirim: ${mesaj}`);}}classSmsBildirimiextendsBildirim {gonder(mesaj:string):void{console.log(`SMS gönderildi (Telefon hatları üzerinden): ${mesaj}`);}}classEmailBildirimiextendsBildirim {gonder(mesaj:string):void{console.log(`E-posta gönderildi (SMTP Sunucusu ile): ${mesaj}`);}}// Çok biçimliliği kullanan fonksiyon: Gelen nesnenin hangi alt sınıf olduğunu önemsemez,// sadece Bildirim ailesinden olduğunu bilir.functiontopluHaberUcur(sistemler: Bildirim[],icerik:string) {sistemler.forEach(sistem => {sistem.gonder(icerik);// Her sınıf kendi ezdiği sürümü çalıştırır!});}constbildirimKanallari: Bildirim[] = [newSmsBildirimi(),newEmailBildirimi()];topluHaberUcur(bildirimKanallari,"Sistem bakımı bu gece yapılacaktır.");/*Çıktı:SMS gönderildi (Telefon hatları üzerinden): Sistem bakımı bu gece yapılacaktır.E-posta gönderildi (SMTP Sunucusu ile): Sistem bakımı bu gece yapılacaktır.*/
Generic
(Genelleyici), yapıları oluştururken kesin bir tip vermek yerine,
tipin ne olacağını kullanılacağı ana (parametre gibi)
erteleyen muazzam bir esneklik özelliğidir. Tip güvenliğini
kaybetmeden her tiple çalışabilen bileşenler üretmemizi sağlar.
<T>
sembolü ile gösterilir.
Gelen verinin türü neyse, fonksiyonun çıktı türünü de otomatik ona eşleyen dinamik fonksiyonlardır.
// <T> harfi dinamik bir tip parametresidir (İstediğiniz harfi verebilirsiniz)functionelementiDondur<T>(eleman: T): T {returneleman;}// Kullanırken tipi açıkça belirtebiliriz:letsonuc1= elementiDondur<string>("Merhaba Isparta");// sonuc1 tipi 'string' olurletsonuc2= elementiDondur<number>(2026);// sonuc2 tipi 'number' olur// Tip çıkarımı sayesinde TypeScript kendi de anlayabilir:letsonuc3= elementiDondur(true);// sonuc3 tipi 'boolean' olur
İçinde tutacağı verilerin türü sonradan belirlenebilen esnek sınıflardır.
classDepo<T> {privateicerik: T[] = [];ekle(yeniEleman: T):void{this.icerik.push(yeniEleman);}listele(): T[] {returnthis.icerik;}}// Sadece sayıları kabul eden bir depo üretiyoruzconstsayiDeposu=newDepo<number>();sayiDeposu.ekle(10);sayiDeposu.ekle(25);// sayiDeposu.ekle("Metin"); // HATA: string kabul edilmez!// Sadece nesneleri kabul eden bir depo üretiyoruzconstkullaniciDeposu=newDepo<{id:number;isim:string}>();kullaniciDeposu.ekle({id:1,isim:"Hakan"});
Kullanılacak nesne modelinin içindeki bazı alanların esnek tipli olmasını istediğimizde imdadımıza yetişir.
// Gelen verinin (data) tipi dinamik olsuninterfaceSunucuCevabi<T> {durumKodu:number;mesaj:string;data: T;// Sunucudan sunucuya değişecek veri yapısı}// Örnek 1: Ürün listesi dönen bir API yanıtıinterfaceUrunModeli{ad:string;fiyat:number}consturunYaniti: SunucuCevabi<UrunModeli[]> = {durumKodu:200,mesaj:"Başarılı",data: [{ad:"Mouse",fiyat:150}]};// Örnek 2: Sadece sayı dizisi dönen bir API yanıtıconstsayiYaniti: SunucuCevabi<number[]> = {durumKodu:200,mesaj:"Başarılı",data: [12,45,78]};
Harika! Eğitim rehberimizin son aşamasına geldik. Müfredatımızın kapanış bölümünde yer alan TypeScript'te Tip Kontrol Mekanizmaları ve TypeScript ile Asenkron Programlama konularını en ince ayrıntısına kadar inceliyor, bolca pratik uygulama ve kod örneğiyle seriyi tamamlıyoruz.
TypeScript bazen bir verinin türünü tam olarak kestiremez veya bizim o verinin türünden %100 emin olduğumuz durumlar olur. Bu durumlarda tip kontrol mekanizmalarını kullanırız.
TypeScript derleyicisine, "Sen
bu verinin tipini tam çözemedin ama bana güven, bunun tipi
kesinlikle şudur"
deme yöntemidir. JavaScript tarafında bir dönüşüm yapmaz,
sadece derleme zamanında TypeScript'i ikna eder. as
anahtar kelimesi ile kullanılır.
// DOM elemanları çekilirken TypeScript bunun hangi HTML etiketi olduğunu bilemez (Sadece Element der)constinputElemani= document.getElementById("kullanici-email")asHTMLInputElement;// 'as HTMLInputElement' dediğimiz için artık 'value' özelliğine hatasız erişebiliriz:if(inputElemani) {console.log(inputElemani.value);}// Alternatif eski yazım tarzı (Açı parantez yöntemi):letbilinmeyenVeri:unknown="Bu bir metindir";letmetinUzunlugu:number= (<string>bilinmeyenVeri).length;
Geniş bir veri türünü (örneğin
string
| number),
kodun ilerleyen satırlarında koşullar kullanarak daha özel/dar
bir veri türüne indirme işlemidir. typeof
ve instanceof
gibi JavaScript operatörleri bu daraltmayı sağlar.
// Örnek 1: İlkel Tiplerde Daraltma (Typeof)functionformatiKoru(deger:string|number) {if(typeofdeger ==="string") {// TypeScript burada 'deger' değişkeninin kesinlikle string olduğunu bilirreturndeger.trim().toUpperCase();}// TypeScript bu satıra gelindiğinde değerin kesinlikle 'number' kaldığını anlarreturndeger.toFixed(2);}// Örnek 2: Sınıflarda Daraltma (Instanceof)classKopek{havla() { console.log("Hav hav!"); } }classKedi{miyavla() { console.log("Miyav!"); } }functionhayvanSeslendir(hayvan: Kopek | Kedi) {if(hayvaninstanceofKopek) {hayvan.havla();// Güvenli}else{hayvan.miyavla();// Güvenli}}
Union (|):
Verinin belirtilen tiplerden herhangi biri olabilmesini
sağlar.
Intersection (&):
Birden fazla tipi birleştirerek, yeni oluşan tipin tüm
özellikleri aynı anda taşımasını zorunlu kılar.
// Intersection (Kesişim) ÖrneğiinterfaceKisi{isim:string;}interfaceCalisanOzellikleri{personelNo:number;maas:number;}// İki arayüzü & ile birleştirerek yeni bir kurumsal kimlik tipi üretiyoruztypeSirketPersoneli = Kisi & CalisanOzellikleri;constmühendis: SirketPersoneli = {isim:"Eren Demir",// Kisi'den geldipersonelNo:404,// CalisanOzellikleri'nden geldimaas:38000// CalisanOzellikleri'nden geldi};
İnternetten veri çekme, dosya okuma gibi zaman alan işlemler asenkron olarak yönetilir. TypeScript, bu süreçteki veri akışını ve hataları da tiplerle koruma altına alır.
Bir asenkron işlemin gelecekte
üreteceği sonucu veya yapacağı hatayı temsil eden nesnelerdir.
TypeScript'te Promise yapısının yanına <T>
generic ifadesini ekleyerek, gelecekte dönecek verinin tipini
taahhüt ederiz.
interfaceUrunVerisi{id:number;ad:string;}// 2 saniye sonra bir ürün döndüreceğini söz veren (Promise) fonksiyonfunctionurunGetir(): Promise<UrunVerisi> {returnnewPromise((resolve,reject) => {setTimeout(() => {constbasariliMi=true;if(basariliMi) {resolve({id:99,ad:"Kablosuz Kulaklık"});}else{reject("Veri çekme hatası oluştu.");}},2000);});}// Promise TüketimiurunGetir().then((urun) => console.log(`Ürün Geldi: ${urun.ad}`))// urun tipi 'UrunVerisi' olarak otomatik bilinir.catch((hata) => console.error(hata));
Promise tabanlı asenkron kodları,
sanki düz ve senkron bir kodmuş gibi yukarıdan aşağıya doğru
düzgünce yazmamızı sağlayan en modern kelime çiftidir. Bir
fonksiyonun içinde await
kullanabilmek için o fonksiyonun başına async
eklenmelidir.
Asenkron işlemlerde internet
kopması, sunucu çökmesi gibi hataları yakalayıp programın
tamamen kapanmasını engellemek için try-catch
blokları kurarız.
interfaceHavaDurumu{sehir:string;derece:number;}asyncfunctionhavaDurumuGoster(): Promise<void> {try{console.log("Hava durumu verisi isteniyor...");// Asenkron işlemi bekliyoruz (Sanki internetten veri çekiyor gibi)constveri: HavaDurumu =awaitnewPromise((resolve) => {setTimeout(() => resolve({sehir:"Isparta",derece:18}),1500);});console.log(`${veri.sehir} için hava sıcaklığı: ${veri.derece}°C`);}catch(hata) {// Eğer bir hata oluşursa kod buraya düşerconsole.log("Hata yakalandı: ", hata);}}havaDurumuGoster();
Öğrendiğiniz tüm bu harika TypeScript konularını pekiştirmeniz için harika bir test ve hemen ardından gerçekçi bir mini proje hazırladım!
Öncelikle, konulardaki bilginizi ölçmek ve nerede eksik olduğunuzu görmek için aşağıdaki interaktif quizi tamamlayabilirsiniz:
TypeScript'te string
| number
şeklinde tanımlanan bir değişken hangi kavramı ifade eder ve
nasıl bir davranış sergiler?
[]
Sadece string veya number değer alabilir, ancak üzerinde işlem yaparken derleyici tür kontrolü yapmaz ve any gibi davranır.
Yanlış. Union tipleri derleme zamanında tam tür güvenliği (type safety) sağlar; tür daraltması (type narrowing) yapılana kadar her iki tipe ortak olan metotlar dışında işlem yapılmasına izin verilmez.
Union type (Birlik tipi) olarak adlandırılır; değişkenin bu tiplerden herhangi birini alabileceğini belirtir ve tür daraltması yapılana kadar her iki tipe ortak olanlar dışındaki metotlara erişimi kısıtlar.
Doğru!
|
operatörü ile birden fazla tipin birleşimi (union) sağlanır ve
TypeScript bu değer üzerinde güvenli operasyonlar yapmanızı
bekler.
Intersection type (Kesişim tipi) olup her iki tipin özelliklerini aynı anda ve zorunlu olarak taşımak zorundadır.
Yanlış.
Kesişim tipleri &
operatörü ile ifade edilir, |
operatörü union (birlik) tipidir.
Tuple tipinin alternatif bir yazımıdır ve dizi elemanlarının sırasını kesin bir şekilde belirler.
Yanlış.
Tuple tanımlamaları köşeli parantez [string,
number]
şeklinde yapılır.
İpucu:
|
operatörünün TypeScript'teki adını ve tür güvenliği
üzerindeki etkisini düşünün.
TypeScript'te fonksiyon aşırı yüklemesi (function overloading) yaparken dikkat edilmesi gereken en temel kural nedir?
[]
Aşırı yüklenen her imza farklı sayıda parametre almak zorundadır ve tipleri tamamen aynı olmak zorundadır.
Yanlış. Farklı tiplerde ve sayılarda imza tanımlanabilir; temel kural parametre tiplerinin veya sayısının çeşitlendirilebilmesidir.
Asıl uygulamanın (implementation) gövdesi, aşırı yükleme imzalarından ayrı olarak farklı bir dosyada export edilmelidir.
Yanlış. Uygulama gövdesi aynı dosya içinde ve imza tanımlarının hemen altında yer alır.
Birden fazla imza (signature) tanımlanmalı ve bu imzaların tümünü karşılayabilecek tek bir genel uygulama gövdesi (implementation body) yazılmalıdır.
Doğru! Overload imzaları dışarıya görünen sözleşmelerdir, en alttaki tek gövde ise tüm bu durumları ele alan gerçek implementasyondur.
Aşırı
yükleme (overload) sadece arrow fonksiyonlar üzerinde
kullanılabilir, klasik function
anahtar kelimesiyle geçersizdir.
Yanlış.
Fonksiyon aşırı yüklemesi genellikle standart function
yapılarıyla kullanılır.
İpucu:
İmza tanımları ile gerçek fonksiyon gövdesinin yapısı ve sayısı arasındaki ilişkiyi hatırlayın.
Bir
interface içinde readonly
anahtar kelimesi kullanıldığında bu property için ne
söylenebilir?
[]
Property sadece nesne ilk oluşturulurken veya atanırken değer alabilir; daha sonra bu property'nin değeri değiştirilemez.
Doğru!
readonly
anahtar kelimesi, nesne gerçekleştikten sonra ilgili alanın
değiştirilmesini (immutability) derleme seviyesinde engeller.
Property'ye sınıf dışından erişilemez, sadece sınıf içindeki metotlar tarafından okunabilir.
Yanlış.
Erişim kısıtlamaları için private
veya protected
kullanılır; readonly
erişimi değil, değerin değiştirilemezliğini ifade eder.
Property isteğe bağlıdır (optional), atanmasa bile arayüz hata vermez.
Yanlış.
İsteğe bağlı alanlar soru işareti (?)
ile belirtilir.
Property sadece sınıf örneklemesi (instantiation) sırasında statik olarak tanımlanabilir, nesne örneklerinde bulunamaz.
Yanlış. Nesne örneklerinde de readonly alanlar tanımlanabilir.
İpucu:
"Readonly" kelimesinin kelime anlamını ve atama işlemlerindeki kısıtlamayı göz önüne alın.
Bir
sınıf içerisinde protected
olarak tanımlanan bir üyeye (property veya metod) nereden
erişilebilir?
[]
Sadece sınıfın dışından ve türetilmiş (child) sınıflardan erişilebilir, tanımlandığı sınıftan erişilemez.
Yanlış. Tanımlandığı sınıfın içinden de erişilebilir.
Sadece tanımlandığı sınıfın içerisinden erişilebilir, türetilmiş alt sınıflar dahi erişemez.
Yanlış.
Sadece sınıf içerisinden erişim kısıtlaması private
kelimesine aittir.
Herhangi
bir yerden doğrudan (global olarak) erişilebilir, public
ile aynı anlama gelir.
Yanlış. Dışarıdan doğrudan erişime kapalıdır.
Tanımlandığı sınıfın içerisinden ve o sınıftan türetilen alt (child) sınıfların içerisinden erişilebilir, ancak sınıf dışından doğrudan erişilemez.
Doğru!
protected
anahtar kelimesi kalıtım (inheritance) zincirindeki alt sınıflara
erişim izni verirken dış dünyayı sınırlar.
İpucu:
private
ile public
arasındaki orta yolu ve kalıtım bağlamını düşünün.
TypeScript'te
abstract
anahtar kelimesiyle tanımlanan soyut sınıflar hakkında
aşağıdakilerden hangisi doğrudur?
[]
Soyut
sınıflar new
anahtar kelimesiyle doğrudan örneklenebilir (instantiate
edilebilir).
Yanlış. Soyut sınıfların doğrudan bir örneği oluşturulamaz, yalnızca alt sınıflara base class olurlar.
Doğrudan örneklenemezler; amaçları diğer alt sınıflar için bir şablon oluşturmaktır ve içinde hem gövdeli hem de gövdesiz (soyut) metotlar barındırabilirler.
Doğru! Soyut sınıflar tam bir nesne üretmek yerine ortak bir mimari ve zorunlu metot sözleşmeleri sunar.
İçerdikleri tüm metotlar mutlaka soyut (gövdesiz) olmak zorundadır, normal metot içeremezler.
Yanlış. Soyut sınıflar standart, gövdeli metotlar da barındırabilir.
Sadece arayüzlerin (interfaces) yerine geçmek için tasarlanmıştır, kalıtım alamazlar veya veremezler.
Yanlış. Kalıtım (extends) mekanizmasının merkezinde yer alırlar.
İpucu:
Soyut
kavramının taslak mantığını ve new
ile kullanımını göz önüne alın.
function
logLength<T extends { length: number }>(arg: T): void
imzasına sahip bir generic fonksiyonda, <T
extends { length: number }>
ifadesi neyi garanti eder?
[]
Fonksiyona
gönderilen arg
argümanının mutlaka .length
özelliğine (property) sahip bir tip olması gerektiğini garanti
eder.
Doğru!
extends
kısıtlaması (constraint), T
tipinin en az {
length: number }
yapısını sağlamasını zorunlu kılar.
Fonksiyonun geri dönüş değerinin mutlaka bir sayı olacağını garanti eder.
Yanlış.
Fonksiyonun geri dönüş tipi :
void
olarak açıkça belirtilmiştir.
T
tipinin sadece primitive (ilkel) string tiplerinden oluşabileceğini
kısıtlar.
Yanlış. Array veya length içeren nesneler de bu kurala uyar, sadece string ile sınırlı değildir.
Fonksiyona yalnızca bir adet parametre gönderilebileceğini zorunlu kılar.
Yanlış. Bu ifade parametre sayısını değil, gönderilen tipin şeklini (shape) sınırlar.
İpucu:
extends
anahtar kelimesinin generic tür üzerindeki sınırlandırma
(constraint) görevini hatırlayın.
TypeScript'te
geri dönüş tipi yanlış belirtilmiş async
function getData(): string
şeklindeki bir imza derleme zamanında nasıl bir hata verir?
[]
Hiçbir hata vermez çünkü async fonksiyonlar dahili olarak otomatik unwrapping yapar.
Yanlış. TypeScript derleyicisi açıkça Promise dönüşü bekler.
Sadece çalışma zamanında (runtime) unhandled rejection hatası fırlatır.
Yanlış. TypeScript statik analiz yaparak bunu derleme aşamasında yakalar.
async
fonksiyonlar her zaman bir Promise
döndürmelidir, bu yüzden geri dönüş tipi string
yerine Promise<string>
olmalıdır şeklinde hata verir.
Doğru! Asenkron fonksiyonlar her zaman bir Promise sarmalayıcısı ile sonuç döndürdüğü için imza buna uygun olmalıdır.
Sadece
void
tipiyle kullanılabilir, string
gibi ilkel tiplerle asla kullanılamaz.
Yanlış.
Promise<string>
şeklinde string veriyi taşıyan bir Promise döndürmek tamamen
yasaldır.
İpucu:
Asenkron operasyonların JavaScript ve TypeScript'teki sarmalayıcı tipini düşünün.
type
Person = { name: string; age: number }; type PersonKeys = keyof
Person;
ifadesinden sonra PersonKeys
tipi hangi değere denk gelir?
[]
"string"
| "number"
Yanlış. Bu property'lerin tipleridir, anahtarları (keys) değildir.
name
veya age
nesnesinin değerlerine karşılık gelen tipler
Yanlış. Değerleri değil, alan isimlerini (keys) union olarak alır.
{
name: string; age: number }
Yanlış. Bu orijinal Person tipidir.
"name"
| "age"
Doğru!
keyof
operatörü bir nesne tipinin anahtarlarını bir string literal
union tipine dönüştürür.
İpucu:
keyof
operatörünün bir nesne tipinin anahtarlarını nasıl bir Union
tipine dönüştürdüğünü hatırlayın.
TypeScript'in
dahili utility tiplerinden biri olan Partial<T>
tipi ne işe yarar?
[]
T
tipindeki tüm property'leri readonly
yapar.
Yanlış.
Readonly yapmak için Readonly<T>
utility tipi kullanılır.
T
tipindeki tüm property'leri isteğe bağlı (optional - ?)
hale getirir.
Doğru!
Partial<T>,
özellikle güncelleme (update/patch) işlemlerinde tüm alanları
isteğe bağlı yapmak için idealdir.
T
tipinden sadece belirli property'leri seçerek yeni bir tip türetir.
Yanlış.
Belirli alanları seçmek için Pick<T,
K>
kullanılır.
T
tipindeki tüm isteğe bağlı property'leri zorunlu hale getirir.
Yanlış.
Zorunlu kılmak için Required<T>
kullanılır.
İpucu:
Kelime
anlamı "kısmi" olan bu yardımcı tipin nesne alanlarına
eklediği soru işaretini (?)
düşünün.
Bir
sınıf bir arayüzü (interface) implements
anahtar kelimesiyle uyguladığında (implement ettiğinde) ne
gerçekleşir?
[]
Sınıf, arayüzdeki metotların gövdesini otomatik olarak devralır ve bizim yazmamıza gerek kalmaz.
Yanlış. Interface'ler gövde (implementation) içermez, sadece imza taşır.
Sınıf, arayüzden tam anlamıyla kalıtım (extends) alır ve private üyelere de doğrudan sahip olur.
Yanlış.
implements
bir sözleşmedir (contract), kalıtım mekanizması değildir.
Sınıf, arayüzde tanımlanan tüm property ve metot imzalarını kendi içerisinde eksiksiz olarak tanımlamak (uygulamak) zorunda kalır.
Doğru!
implements
anahtar kelimesi sınıfa bir sözleşme dayatır ve arayüzdeki tüm
üyelerin implement edilmesini zorunlu kılar.
Arayüz, sınıfın tüm özel (private) metotlarını dışarıya açık hale getirir.
Yanlış. Arayüzler private üyeleri kapsayamaz, sadece public imzaları kabul eder.
İpucu:
Sözleşme
(contract) mantığını ve implements
kelimesinin taahhüt ettiği zorunlulukları düşünün.
TypeScript Temelleri ve İleri Seviye Özellikler Quizi
[]
Öğrendiğimiz Interface, Class, Enum, Access Modifiers (Erişim Belirleyiciler) ve Generic yapıları bir araya getiren gerçekçi bir terminal/konsol tabanlı Todo uygulaması mimarisi aşağıdadır. Bu projeyi kendi bilgisayarınızda derleyip çalıştırabilirsiniz.
todo.ts)// 1. Görev Durumları İçin Enum YapısıenumGorevDurumu{Beklemede="Beklemede",DevamEdiyor="Devam Ediyor",Tamamlandi="Tamamlandı"}// 2. Görev Nesnesi İçin Interface (Sözleşme)interfaceIGorev{readonlyid:number;// ID bir kez verilir, değiştirilemezbaslik:string;aciklama?:string;// İsteğe bağlı (Opsiyonel) alandurum: GorevDurumu;}// 3. Görev Yönetimini Üstlenen Sınıf (Class) ve Generic Kullanımı// Veri kaydetme mantığını esnek tutmak için Generic bir yapı kuruyoruzclassGorevYöneticisi<TextendsIGorev> {// Kapsülleme (Encapsulation): Görev listesine dışarıdan doğrudan müdahale edilemezprivategorevler: T[] = [];privatesonrakiId:number=1;// Yeni Görev Ekleme MetodupublicgorevEkle(baslik:string,aciklama?:string):void{// Yeni bir görev nesnesi oluşturuyoruz (Kısmi olarak T tipine zorluyoruz)constyeniGorev= {id:this.sonrakiId++,baslik: baslik,aciklama: aciklama,durum: GorevDurumu.Beklemede}asT;this.gorevler.push(yeniGorev);console.log(`✅ Başarılı: "${baslik}" görevi listeye eklendi.`);}// Görev Durumunu Güncelleme Metodu (Type Narrowing / Güvenlik Kontrolü)publicdurumGuncelle(id:number,yeniDurum: GorevDurumu):void{constgorev=this.gorevler.find(g => g.id === id);if(gorev) {gorev.durum = yeniDurum;console.log(`🔄 Güncellendi: ID ${id} olan görevin yeni durumu -> ${yeniDurum}`);}else{console.log(`❌ Hata: ID ${id} ile eşleşen bir görev bulunamadı!`);}}// Görevleri Listeleme Metodu (ForEach kullanımı)publicgorevleriListele():void{if(this.gorevler.length ===0) {console.log("ℹ️ Listenizde henüz hiç görev bulunmuyor.");return;}console.log("\n📋 --- GÖREV LİSTENİZ ---");this.gorevler.forEach((gorev) => {constdetay= gorev.aciklama ?`(${gorev.aciklama})`:"";console.log(`[ID: ${gorev.id}] - ${gorev.baslik} ${detay} -> Durum: [${gorev.durum}]`);});console.log("-------------------------\n");}}// --- UYGULAMANIN ÇALIŞTIRILMASI (TEST ETME) ---// Sınıfımızdan yeni bir kopya (nesne) üretiyoruzconstuygulamam=newGorevYöneticisi<IGorev>();// 1. Görevleri ekleyelimuygulamam.gorevEkle("TypeScript Çalış","Müfredattaki interface konusunu tekrar et.");uygulamam.gorevEkle("Spora Git");uygulamam.gorevEkle("Kitap Oku","En az 20 sayfa.");// 2. İlk listeyi görelimuygulamam.gorevleriListele();// 3. Bazı görevlerin durumlarını güncelleyelimuygulamam.durumGuncelle(1, GorevDurumu.DevamEdiyor);uygulamam.durumGuncelle(3, GorevDurumu.Tamamlandi);// 4. Hatalı ID denemesi yapalım (Güvenlik kontrolü testi)uygulamam.durumGuncelle(999, GorevDurumu.Tamamlandi);// 5. Güncel listeyi tekrar ekrana basalımuygulamam.gorevleriListele();
Bilgisayarınızda
boş bir klasörde todo.ts
dosyası açın ve yukarıdaki kodları yapıştırın.
Terminalden
tsc
todo.ts komutunu vererek kodu JavaScript'e
dönüştürün (todo.js
dosyası oluşacaktır).
Ardından
terminale node
todo.js yazarak uygulamanın konsol çıktılarını
canlı olarak inceleyin!
***************************************************************************************************
Bu rehberde, paylaştığın metindeki TypeScript konularını adım adım, derinlemesine ve bolca kod örneğiyle inceliyoruz. İstediğin gibi dakika/saniye bilgilerini, "Önizleme" ve "ders" satırlarını temizledim; anlamsız satırları eledim.
Dosya çok kapsamlı olduğu için içeriği "Giriş ve Temel Kavramlar", "Core (Çekirdek) TypeScript" ve "Advanced (İleri Düzey) TypeScript" olarak parçalara ayırdım. İlk olarak kursun amacını açıklayıp ardından konuların detaylı anlatımına geçiyoruz.
Bu kurs, yazılımcıları TypeScript kodlama mülakatlarına (coding interviews) en üst seviyede hazırlamayı amaçlar. Katılımcılar, mülakatlarda karşılarına çıkabilecek teorik ve pratik soruları önceden görerek deneyim kazanırlar. Kurs boyunca sadece ezber bilgi verilmez; TypeScript'in derin mimari kavramları, tip güvenliği (type safety) ve kod kalitesini artıran teknikler uygulamalı olarak aktarılır. Amaç, adayın mülakata girdiğinde kendine tam güven duymasını ve teknik sorulara profesyonel çözümler üretebilmesini sağlamaktır.
JavaScript
(JS): Dinamik tipli bir dildir. Değişkenlerin tipi çalışma
zamanında (runtime) belirlenir. Bu durum büyük projelerde gözden
kaçan hatalara (örneğin; undefined
is not a function) yol açar.
TypeScript (TS): JavaScript'in üzerine inşa edilmiş bir üst kümedir (superset). Statik tiplidir. Kod henüz çalışmadan, derleme (compile) aşamasında hataları yakalar.
Hayır, TS sihirli bir değnek değildir. Yazdığınız kötü algoritmayı kendiliğinden düzeltmez. Ancak projenize entegre edildiği andan itibaren:
Otomatik tamamlamayı (IntelliSense) güçlendirir.
Yanlış parametre gönderimlerini anında engeller.
Büyük refatör (kod yenileme) işlemlerini güvenli hale getirir.
TypeScript'te en temel yapılar
string,
number
ve boolean
değerleridir.
// Uygulamaletage:number=26;letdeveloperName:string="Ahmet";letisTypeScriptAwesome:boolean=true;
Explicit
(Açık): Tipi sizin elle yazmanızdır. (let
x: number = 5;)
Implicit (Örtük/Type Inference): TS'in değeri okuyarak tipi kendisinin tahmin etmesidir.
// Uygulamaletcountry="Türkiye";// TS bunun string olduğunu otomatik anlar (Implicit)// country = 123; // Hata verir! Çünkü o artık bir string.
getFullName
Fonksiyonu ÖrneğiBir fonksiyonun parametrelerini ve dönüş tipini doğru şekilde tanımlama mülakatlarda sıkça istenir.
// UygulamafunctiongetFullName(firstName:string,lastName:string):string{return`${firstName} ${lastName}`;}constresult= getFullName("Can","Yılmaz");
Her ikisi de nesnelerin (object) şeklini tanımlamak için kullanılır.
Interface:
extends
ile genişletilebilir. Aynı isimde iki interface tanımlanırsa TS
bunları otomatik birleştirir (Declaration Merging).
Type:
Nesnelerin yanı sıra primitive (ilkel) tipler, union'lar ve
tuple'lar için de takma ad (alias) oluşturabilir. &
(intersection) ile birleştirilir.
// Interface ÖrneğiinterfaceUser{id:number;}interfaceUser{// Birleştirme (Merging)name:string;}// Type ÖrneğitypePoint = {x:number;y:number;};typeID =string|number;// Interface bunu yapamaz
Union, bir değişkenin
birden fazla tipte olabilmesini sağlar (|).
Narrowing ise bu
geniş tipi typeof
gibi kontrol araçlarıyla tek bir tipe indirme işlemidir.
// UygulamafunctionprintId(id:string|number) {if(typeofid ==="string") {// Burada id kesinlikle string'dir. string metotları kullanılabilir.console.log(id.toUpperCase());}else{// Burada id kesinlikle number'dır.console.log(id.toFixed(2));}}
Void: Fonksiyonun geriye hiçbir değer döndürmediğini belirtir.
Never: Fonksiyonun hiçbir zaman sonlanmadığını (sonsuz döngü veya hata fırlatma) ifade eder.
Any: Tip kontrolünü tamamen kapatır. Güvensizdir, mülakatlarda "kullanılmaması" önerilir.
Unknown:
"Ne olduğunu henüz bilmiyorum" demektir. any
gibidir ama içindeki bir metodu kullanmadan önce tipi daraltmanız
(narrowing) şarttır. Güvenlidir.
// UygulamafunctionlogMessage(msg:string):void{console.log(msg);}functionthrowError(errorMsg:string):never{thrownewError(errorMsg);}letdata:unknown="Merhaba";// data.toUpperCase(); // HATA! Unknown doğrudan kullanılamaz.if(typeofdata ==="string") {console.log(data.toUpperCase());// Doğru kullanım}
HTML elementlerine erişirken doğru elementi cast etmek (Tiplemek) gerekir.
// DOM UygulamasıconstinputElement= document.querySelector(".user-email")asHTMLInputElement;console.log(inputElement.value);// as HTMLInputElement sayesinde .value hata vermez// Class UygulamasıclassEmployee{protectedid:number;publicname:string;constructor(id:number,name:string) {this.id = id;this.name = name;}}
Enum: İsimlendirilmiş sabit değerler kümesidir.
Tuple: Eleman sayısı ve elemanlarının tipleri sabit olan özel dizilerdir.
// EnumenumRole{Admin="ADMIN",User="USER"}// Tupleletproduct: [number,string,boolean];product = [1,"Laptop",true];// Sıra ve tip tam uymalıdır
Fonksiyonların veya bileşenlerin, içine gönderilen tipe göre esnek ve yeniden kullanılabilir olmasını sağlar.
// UygulamafunctiongetFirstElement<T>(arr: T[]): T {returnarr[0];}constnumArr= [1,2,3];constfirstNum= getFirstElement(numArr);// TS otomatik olarak T'nin 'number' olduğunu anlar.
Mülakatların en sevilen pratik soruları bu yardımcı tiplerden gelir:
Optional
Property (?):
Bir alanın zorunlu olmadığını belirtir.
Index Signature & Record: Dinamik anahtarlı nesneler üretir.
Omit /
Pick: Mevcut bir tipten bazı alanları çıkarmak (Omit)
veya seçmek (Pick)
için kullanılır.
Readonly: Değerlerin sonradan değiştirilmesini engeller.
Partial /
Required: Tüm alanları isteğe bağlı (Partial)
veya zorunlu (Required)
yapar.
interfaceCar{brand:string;model:string;year:number;}// UygulamalartypeCarBrandAndModel = Pick<Car,"brand"|"model">;// Sadece brand ve model vartypeCarWithoutYear = Omit<Car,"year">;// year hariç hepsi vartypeOptionalCar = Partial<Car>;// Tüm alanlar '?' oldu// Record Örneği (Dinamik anahtarlar)constuserRoles: Record<string,string[]> = {"ahmet": ["admin","editor"],"mehmet": ["user"]};
React bileşenlerinde (Components) prop'ları tiplemek için interface kullanılır.
interfaceButtonProps{label:string;onClick: () =>void;}// React Component ÖrneğiconstMyButton: React.FC<ButtonProps> = ({label,onClick}) => {return<button onClick={onClick}>{label}</button>;};
tsconfig.json,
d.ts,
.map
ve TranspilationTranspilation:
TS kodunun tsc
derleyicisi yardımıyla tarayıcıların anlayacağı JS koduna
dönüştürülmesidir.
tsconfig.json: Derleyicinin kurallarını (Strict modu, çıktı klasörü vb.) belirleyen ayar dosyasıdır.
d.ts (Declaration File): Sadece tip tanımlamalarını barındıran, gerçek JS kodu üretmeyen dosyalardır.
map dosyası: Tarayıcıda JS hatası aldığınızda, hatanın TS'te tam hangi satıra denk geldiğini gösteren köprü dosyasıdır (debugging için).
Aynı isimde fakat farklı parametre sayıları veya tipleri alan fonksiyonlar tasarlamaktır.
// İmza (Overload Signatures)functionmakeDate(timestamp:number): Date;functionmakeDate(m:number,d:number,y:number): Date;// Gerçekleşme (Implementation)functionmakeDate(mOrTimestamp:number,d?:number,y?:number): Date {if(d !== undefined && y !== undefined) {returnnewDate(y, mOrTimestamp, d);}else{returnnewDate(mOrTimestamp);}}
extends
ve infer
Anahtar Kelimeleri (Conditional Types)
İleri düzey mülakatlarda
"Conditional Types" yani koşullu tipler sorulur. extends
burada bir tipin diğerini kapsayıp kapsamadığını kontrol eder
(if
gibi). infer
ise koşulun içinde bir tipi TS'in tahmin etmesini ve yakalamasını
sağlar.
// infer Uygulaması: Bir fonksiyonun dönüş tipini bulmatypeMyReturnType<T> = Textends(...args:any[]) =>inferR? R :any;constsayHello= () =>"Merhaba";typeHelloReturn = MyReturnType<typeofsayHello>;// 'string' tipini elde ederiz.
Mülakatlarda sıkça sorulan "TypeScript'in kendi dahili tiplerini sıfırdan yazın" meydan okumaları:
Do it yourself - Readonly:
typeMyReadonly<T> = {readonly[PinkeyofT]: T[P];};
Do it yourself - First (Dizinin ilk elemanının tipini alma):
type First<T extends any[]> = T extends [] ? never : T[0];Do it yourself - Tuple Length (Tuple uzunluğunu bulma):
type Length<T extends readonly any[]> = T['length'];Do it yourself - If (Tip bazlı If koşulu):
type If<C extends boolean, T, F> = C extends true ? T : F;Do it yourself - Concat (İki tuple yapısını birleştirme):
type Concat<T extends any[], U extends any[]> = [...T, ...U];