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.


🎯 "Öğrenecekleriniz" Paragrafının Detaylı Açıklaması

Bu paragraf, kursun size kazandıracağı temel yetenekleri ve TypeScript'in neden var olduğunu anlatır. Maddeler halinde ne anlama geldiklerini inceleyelim:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.


🛠️ Bölüm 1: Kurulumlar ve Çalışma Ortamı

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.

TypeScript ve VS Code Kurulumu

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 typescript

Kod 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:

Uygulama: İlk Kodun Derlenmesi

Boş bir klasör oluşturun ve içine app.ts isimli bir dosya açıp şu kodu yazın:

let mesaj: 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.


📊 Bölüm 2: Data Türleri (Temel Seviye)

TypeScript'in en güçlü tarafı değişkenlerin hangi türde veri tutacağını kesin olarak belirleyebilmemizdir.

1. Number, String ve Boolean

En temel veri yapıları sayısal, metinsel ve mantıksal değerlerdir.

Kod Örneği:

let yas: number = 26;
let isim: string = "Ahmet";
let ogrenciMi: boolean = true;

// Hata Durumu (TypeScript burada hemen kırmızı çizgi çeker):
// yas = "Yirmi Altı"; // HATA: 'string' türü 'number' türüne atanamaz!

2. Tip Ataması (Type Assignment) & Tip Çıkarımı (Type Inference)

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.

Kod Örneği:

let sehir = "Isparta"; // TypeScript bunun 'string' olduğunu otomatik anlar.
// sehir = 32; // HATA verir!

// Açıkça belirtmek istersek (Tip Ataması):
let plaka: number = 32;

3. Object Types (Nesne Tipleri)

Bir nesnenin içindeki alt özelliklerin de hangi tipte olacağını tek tek belirleyebiliriz.

Uygulama: Kullanıcı Kartı Oluşturma

let kullanici: { 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!

4. Array Types (Dizi Tipleri)

Bir dizinin sadece belirli bir tipte veri kabul etmesini sağlarız.

Uygulama: Hobi Listesi ve Metin Dönüşümü

let hobiler: 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...
});

🚀 Bölüm 3: Gelişmiş Veri Türleri

TypeScript, standart JavaScript'te olmayan veya JavaScript'i daha güvenli hale getiren özel gelişmiş tipler sunar.

1. Tuples Type (Sabit Diziler)

Eleman sayısı ve hangi indekste hangi tipin olacağı kesin olarak belli olan özel dizilerdir.

Kod Örneği: HTTP Durum Kodu Yönetimi

let apiCevabi: [number, string] = [200, "Başarılı"];

// apiCevabi = ["Hata", 404]; // HATA: Sıralama yanlış! Sayı önce gelmeli.

2. Enum Type (Numaralandırma)

Kod içinde sürekli kullanılan sabit kelime gruplarını (örneğin roller, günler, yönler) merkezi bir yapıda toplar.

Uygulama: Kullanıcı Rol Yönetimi

enum Rol {
    Admin,    // Arka planda 0 değerini alır
    Editor,   // Arka planda 1 değerini alır
    Uye       // Arka planda 2 değerini alır
}

let mevcutKullaniciRolü: Rol = Rol.Admin;

if (mevcutKullaniciRolü === Rol.Admin) {
    console.log( "Yönetim paneline erişim izni verildi.");
}

3. Any ve Unknown Tipleri

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.

Kod Örneği:

let rastgeleVeri: any = 10;
rastgeleVeri = "Şimdi metin oldum"; // Hata vermez, serbesttir.

let belirsizVeri: unknown = "Merhaba";
// let metinUzunlugu: number = belirsizVeri.length; // HATA: Tipi garanti etmeden kullanamazsın!

// Güvenli kontrol (Type Guard):
if (typeof belirsizVeri === "string") {
    console.log(belirsizVeri.length); // Burada artık güvenli!
}

4. Union (Birlik) ve Literal Tipler

Uygulama: Kargo Durum Takibi

// Sadece bu üç metinden biri girilebilir (Literal + Union):
let kargoDurumu: "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:
let KimlikNo: string | number;
KimlikNo = 123456;
KimlikNo = "TR123456";

5. Void ve Never Tipleri

Kod Örneği:

function logMesaj(mesaj: string): void {
    console.log("Sistem Mesajı: " + mesaj);
    // return 5; // HATA: void fonksiyon bir şey döndüremez!
}

function hataFirlat(mesaj: string): never {
    throw new Error(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.


🎯 Bölüm 4: Functions (Fonksiyonlar)

TypeScript'te fonksiyonlar, hem parametrelerin (girdi) hem de fonksiyonun üreteceği sonucun (çıktı) tip güvenliğini korumak için tasarlanmıştır.

1. Arrow Functions (Ok Fonksiyonları)

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.

Uygulama: Fiyat Hesaplama ve Filtreleme

// İki sayıyı çarpan basit bir arrow fonksiyonu
const kdvHesapla = (fiyat: number, oran: number): number => {
    return fiyat * oran;
};

// Tek satırlık yazım (Süslü parantez ve return yazılmazsa otomatik döner)
const ikiKatiniAl = (sayi: number): number => sayi * 2;

// Dizilerde callback (geri çağırım) olarak kullanımı
const urunFiyatlari: number[] =;
const ucuzUrunler = urunFiyatlari.filter(fiyat => fiyat < 400); 
// Çıktı: [100, 250]

2. Optional (Opsiyonel) & Default (Varsayılan) Parameters

Uygulama: Kullanıcı Karşılama Sistemi

// 'unvan' opsiyoneldir, 'rol' ise varsayılan olarak "Üye" tanımlanmıştır
function kullaniciSelamla(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"

3. Callback Functions (Geri Çağırım Fonksiyonları)

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.

Uygulama: Matematiksel İşlem Merkezi

// callback fonksiyonunun yapısı: iki sayı almalı ve sayı döndürmeli
function islemMerkezi(a: number, b: number, islem: (x: number, y: number) => number): void {
    const sonuc = islem(a, b);
    console.log(`İşlem Sonucu: ${sonuc}`);
}

const toplama = (s1: number, s2: number): number => s1 + s2;
const carpma = (s1: number, s2: number): number => s1 * s2;

islemMerkezi(5, 3, toplama); // Çıktı: İşlem Sonucu: 8
islemMerkezi(5, 3, carpma);  // Çıktı: İşlem Sonucu: 15

4. Function Overloading (Fonksiyon Aşırı Yüklemesi)

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.

Uygulama: Esnek Mesaj Gösterici

// 1. İmza: Sadece tek bir metin alırsa
function mesajYaz(metin: string): void;

// 2. İmza: Bir metin dizisi alırsa
function mesajYaz(metinler: string[]): void;

// 3. Gerçek Gövde (Implementation): Yukarıdaki tüm imzaları karşılamalıdır
function mesajYaz(veri: any): void {
    if (typeof veri === "string") {
        console.log(`Tek Mesaj: ${veri}`);
    } else if (Array.isArray(veri)) {
        console.log(`Çoklu Mesajlar: ${veri.join(" - ")}`);
    }
}

mesajYaz("Merhaba!"); // Geçerli
mesajYaz(["Hata 1", "Hata 2", "Hata 3"]); // Geçerli

5. Rest Parameters (Kalan Parametreler)

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.

Kod Örneği: Sınırsız Sayı Toplama

function hepsiniTopla(ilkSayi: number, ...kalanSayilar: number[]): number {
    let toplam = ilkSayi;
    kalanSayilar.forEach(sayi => toplam += sayi);
    return toplam;
}

console.log(hepsiniTopla(10, 20, 30, 40, 50)); // Çıktı: 150

🔄 Bölüm 5: Control Flow Statements (Kontrol Akış Yapıları ve Döngüler)

Kodun hangi şartlarda nereye sapacağını (koşullar) veya bir işlemi kaç kez tekrar edeceğini (döngüler) belirleyen yapılardır.

1. If - Else - Ternary Operators

Kod Örneği:

let bakiye: number = 500;
let urunFiyati: number = 600;

// Klasik If - Else
if (bakiye >= urunFiyati) {
    console.log("Satın alabilirsiniz.");
} else {
    console.log("Bakiye yetersiz.");
}

// Ternary Operatörü ile kısa yazım
let sonucMesaji: string = bakiye >= urunFiyati ? "Onaylandı" : "Reddedildi";

2. Switch - Case

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.

Uygulama: Trafik Lambası Kontrolü

enum Isik {
    Kirmizi,
    Sari,
    Yesil
}

let mevcutIsik: Isik = Isik.Sari;

switch (mevcutIsik) {
    case Isik.Kirmizi:
        console.log("Dur!");
        break;
    case Isik.Sari:
        console.log("Hazırlan!");
        break;
    case Isik.Yesil:
        console.log("Geç!");
        break;
    default:
        console.log("Bilinmeyen lamba durumu.");
}

3. For ve While Loops (Döngüler)

Kod Örneği:

// For Döngüsü ile 1'den 3'e kadar sayma
for (let i: number = 1; i <= 3; i++) {
    console.log(`For Sayacı: ${i}`);
}

// While Döngüsü ile geri sayım
let sayac: number = 3;
while (sayac > 0) {
    console.log(`While Sayacı: ${sayac}`);
    sayac--; // Şartı bozmak için sayacı azaltıyoruz
}

4. ForEach Döngüsü

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.

Uygulama: Ürün Nesneleri Dizisini Listeleme

// Nesnelerden oluşan bir dizi tanımlayalım
let sepet: { 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ım
sepet.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: 1
2. Sıradaki Ürün: Fare (Mouse) - Adet: 2
3. 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.


🎯 Bölüm 6: TypeScript'te Interface Kullanımı

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.

1. Interface Tanımlama ve Kullanımı

Bir nesnenin uyması gereken zorunlu alanları belirleriz.

Uygulama: Personel Veri Kartı

// Sözleşmeyi imzalıyoruz
interface Calisan {
    id: number;
    isim: string;
    departman: string;
}

// Nesnemizi oluştururken bu sözleşmeye uymak zorundayız
const yeniCalisan: 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!

2. Fonksiyonel Interface'ler (Functional Interfaces)

Interface'ler sadece nesne özelliklerini değil, bir fonksiyonun alacağı parametreleri ve döneceği veri tipini de kurala bağlayabilir.

Uygulama: Matematiksel Hesap Sözleşmesi

interface MatematikIslemi {
    (sayi1: number, sayi2: number): number; // İki sayı alıp bir sayı dönmeli
}

// Sözleşmeye uygun bir fonksiyon yazıyoruz
const bolmeIslemi: MatematikIslemi = (x, y) => {
    if (y === 0) return 0;
    return x / y;
};

3. Interface'lerde Opsiyonel (?) Özellikler

Bazı verilerin girilmesi zorunlu olmayabilir. Tıpkı değişkenlerde olduğu gibi ? işareti koyarak bir alanı isteğe bağlı yapabiliriz.

Uygulama: Ürün Detay Şablonu

interface Urun {
    id: number;
    ad: string;
    fiyat: number;
    renk?: string; // Bu alan girilmese de TypeScript hata vermez
}

const urun1: Urun = { id: 1, ad: "Klavye", fiyat: 450 }; // Geçerli
const urun2: Urun = { id: 2, ad: "Telefon", fiyat: 15000, renk: "Siyah" }; // Geçerli

4. Readonly (Salt Okunur) Özellikler & Readonly Array

Kod Örneği:

interface Ayar {
    readonly apiEndpoint: string; // Değiştirilemez!
    versiyon: string;
}

const sistemAyari: 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ği
let korumaliSayilar: readonly number[] =;
// korumaliSayilar.push(40); // HATA: 'push' metodu bu dizi için kullanılamaz!

5. Extending Interfaces (Interface Miras Alma)

Bir interface, başka bir interface'in kurallarını miras alarak kendi üzerine ekleyebilir. Böylece kod tekrarı yapmamış oluruz.

Uygulama: Kullanıcı Rol Hiyerarşisi

interface StandartKullanici {
    id: number;
    email: string;
}

// Admin, standart kullanıcının tüm özelliklerine ek olarak yetki seviyesine sahip olur
interface AdminKullanici extends StandartKullanici {
    yetkiSeviyesi: number;
    sistemLoglariniOku(): void;
}

const admin: AdminKullanici = {
    id: 1,
    email: "admin@sirket.com",
    yetkiSeviyesi: 5,
    sistemLoglariniOku() {
        console.log("Loglar okunuyor...");
    }
};

🏢 Bölüm 7: TypeScript'te Class (Sınıf) Kullanımı

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.

1. Class & Constructor Functions (Kurucu Fonksiyonlar)

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.

Uygulama: Araba Modeli Üretimi

class Araba {
    marka: string; // Sınıf özelliği (Property)
    yil: number;

    // Kurucu Fonksiyon
    constructor(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ı kullanma
const benimArabam = new Araba("Toyota", 2022);
benimArabam.bilgiVer(); // Çıktı: Bu araç bir Toyota ve 2022 modeldir.

2. Erişim Belirleyiciler: Public, Private & Protected

Sınıfın içindeki verilere dışarıdan kimlerin erişebileceğini belirleyen güvenlik duvarlarıdır.

Uygulama: Banka Hesabı Güvenlik Mimarisi

class BankaHesabi {
    public musteriAdi: string;         // Herkes görebilir
    private bakiye: number;             // Sadece bu sınıf değiştirebilir
    protected subeKodu: string;         // Bu sınıf ve çocukları görebilir

    constructor(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ıyoruz
    public paraYatir(miktar: number): void {
        if (miktar > 0) {
            this.bakiye += miktar;
            console.log(`${miktar} TL yatırıldı. Yeni bakiye: ${this.bakiye}`);
        }
    }
}

const hesap = new BankaHesabi("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

3. Class Methods (Sınıf Metotları) & 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.

Kod Örneği:

class Sayac {
    deger: number = 0;

    artir(): void {
        this.deger++; // this olmadan 'deger' değişkenini bulamaz
    }

    mevcutDeger(): void {
        console.log(`Güncel Sayı: ${this.deger}`);
    }
}

constislem = new Sayac();
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ölüm 8: Inheritance ve Polymorphism (Miras Alma ve Çok Biçimlilik)

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.

1. Inheritance (Miras) ve 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.

Uygulama: Şirket Çalışan Hiyerarşisi

// Ana Sınıf (Base Class)
class Personel {
    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ıyor
class Yoneticis extends Personel {
    ekipBoyutu: number;

    constructor(ad: string, maas: number, ekipBoyutu: number) {
        super(ad, maas); // Ana sınıfın constructor'ını çağırıyoruz
        this.ekipBoyutu = ekipBoyutu; // Kendine ait yeni özellik
    }

    // Metot Ezme (Method Overriding): Ana sınıftaki metodu yöneticiye özel değiştirmek
    calis(): void {
        console.log(`${this.ad}, ${this.ekipBoyutu} kişilik ekibini yönetiyor.`);
    }

    toplantiYap(): void {
        console.log(`${this.ad} strateji toplantısı başlattı.`);
    }
}

const mudur = new Yoneticis("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ı.

2. Soyut Sınıflar (Abstract Classes) ve Interface'ler ile Çalışma

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.

Uygulama: Veritabanı Entegrasyon Şablonu

// Soyut Sınıf
abstract class Veritabani {
    // 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)
    abstract veriKaydet(veri: string): void;
}

// Alt Sınıf 1
class MySqlVeritabani extends Veritabani {
    veriKaydet(veri: string): void {
        console.log(`'${veri}' verisi MySQL kurallarına göre kaydedildi.`);
    }
}

// Alt Sınıf 2
class MongoVeritabani extends Veritabani {
    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!
const mysql = new MySqlVeritabani();
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.

3. Polymorphism (Çok Biçimlilik) Kavramı

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.

Uygulama: Bildirim (Notification) Sistemi

class Bildirim {
    gonder(mesaj: string): void {
        console.log(`Genel Bildirim: ${mesaj}`);
    }
}

class SmsBildirimi extends Bildirim {
    gonder(mesaj: string): void {
        console.log(`SMS gönderildi (Telefon hatları üzerinden): ${mesaj}`);
    }
}

class EmailBildirimi extends Bildirim {
    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.
function topluHaberUcur(sistemler: Bildirim[], icerik: string) {
    sistemler.forEach(sistem => {
        sistem.gonder(icerik); // Her sınıf kendi ezdiği sürümü çalıştırır!
    });
}

const bildirimKanallari: Bildirim[] = [new SmsBildirimi(), new EmailBildirimi()];
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.
*/

🎯 Bölüm 9: TypeScript Generic'leri ve Tip Yönetimi

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.

1. Generic Fonksiyonlar

Gelen verinin türü neyse, fonksiyonun çıktı türünü de otomatik ona eşleyen dinamik fonksiyonlardır.

Uygulama: Güvenli Kutu (Cevap Döndürücü)

// <T> harfi dinamik bir tip parametresidir (İstediğiniz harfi verebilirsiniz)
function elementiDondur<T>(eleman: T): T {
    return eleman;
}

// Kullanırken tipi açıkça belirtebiliriz:
let sonuc1 = elementiDondur<string>("Merhaba Isparta"); // sonuc1 tipi 'string' olur
let sonuc2 = elementiDondur<number>(2026);              // sonuc2 tipi 'number' olur

// Tip çıkarımı sayesinde TypeScript kendi de anlayabilir:
let sonuc3 = elementiDondur(true);                      // sonuc3 tipi 'boolean' olur

2. Generic Sınıflar (Generic Classes)

İçinde tutacağı verilerin türü sonradan belirlenebilen esnek sınıflardır.

Uygulama: Veri Depolama Kutusu (Data Storage)

class Depo<T> {
    private icerik: T[] = [];

    ekle(yeniEleman: T): void {
        this.icerik.push(yeniEleman);
    }

    listele(): T[] {
        return this.icerik;
    }
}

// Sadece sayıları kabul eden bir depo üretiyoruz
const sayiDeposu = new Depo<number>();
sayiDeposu.ekle(10);
sayiDeposu.ekle(25);
// sayiDeposu.ekle("Metin"); // HATA: string kabul edilmez!

// Sadece nesneleri kabul eden bir depo üretiyoruz
const kullaniciDeposu = new Depo<{ id: number; isim: string }>();
kullaniciDeposu.ekle({ id: 1, isim: "Hakan" });

3. Generic Interface'ler (Generic Interfaces)

Kullanılacak nesne modelinin içindeki bazı alanların esnek tipli olmasını istediğimizde imdadımıza yetişir.

Uygulama: API Sunucu Yanıt Şablonu

// Gelen verinin (data) tipi dinamik olsun
interface SunucuCevabi<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ı
interface UrunModeli { ad: string; fiyat: number }
const urunYaniti: SunucuCevabi<UrunModeli[]> = {
    durumKodu: 200,
    mesaj: "Başarılı",
    data: [{ ad: "Mouse", fiyat: 150 }]
};

// Örnek 2: Sadece sayı dizisi dönen bir API yanıtı
const sayiYaniti: 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.


🎯 Bölüm 10: TypeScript'te Tip Kontrol Mekanizmaları

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.

1. Type Assertion (Tip Bildirimi / Zorlaması)

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.

Uygulama: HTML Sayfasından Eleman Yakalama

// DOM elemanları çekilirken TypeScript bunun hangi HTML etiketi olduğunu bilemez (Sadece Element der)
const inputElemani = document.getElementById("kullanici-email") as HTMLInputElement;

// '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):
let bilinmeyenVeri: unknown = "Bu bir metindir";
let metinUzunlugu: number = (<string>bilinmeyenVeri).length;

2. Type Guards & Type Narrowing (Tip Muhafızları ve Daraltma)

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.

Uygulama: Gelişmiş Hesaplama ve Nesne Kontrolü

// Örnek 1: İlkel Tiplerde Daraltma (Typeof)
function formatiKoru(deger: string | number) {
    if (typeof deger === "string") {
        // TypeScript burada 'deger' değişkeninin kesinlikle string olduğunu bilir
        return deger.trim().toUpperCase();
    }
    // TypeScript bu satıra gelindiğinde değerin kesinlikle 'number' kaldığını anlar
    return deger.toFixed(2);
}

// Örnek 2: Sınıflarda Daraltma (Instanceof)
class Kopek { havla() { console.log("Hav hav!"); } }
class Kedi { miyavla() { console.log("Miyav!"); } }

function hayvanSeslendir(hayvan: Kopek | Kedi) {
    if (hayvan instanceof Kopek) {
        hayvan.havla(); // Güvenli
    } else {
        hayvan.miyavla(); // Güvenli
    }
}

3. Union & Intersection Types (Birlik ve Kesişim Tipleri)

Uygulama: Sistem Kayıt ve Kimlik Mimarisi

// Intersection (Kesişim) Örneği
interface Kisi {
    isim: string;
}

interface CalisanOzellikleri {
    personelNo: number;
    maas: number;
}

// İki arayüzü & ile birleştirerek yeni bir kurumsal kimlik tipi üretiyoruz
type SirketPersoneli = Kisi & CalisanOzellikleri;

const mühendis: SirketPersoneli = {
    isim: "Eren Demir",     // Kisi'den geldi
    personelNo: 404,        // CalisanOzellikleri'nden geldi
    maas: 38000            // CalisanOzellikleri'nden geldi
};

🌐 Bölüm 11: TypeScript ile Asenkron Programlama

İ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.

1. Promise Kullanımı

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.

Uygulama: Sunucudan Ürün Getirme Simülasyonu

interface UrunVerisi {
    id: number;
    ad: string;
}

// 2 saniye sonra bir ürün döndüreceğini söz veren (Promise) fonksiyon
function urunGetir(): Promise<UrunVerisi> {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const basariliMi = true;
            if (basariliMi) {
                resolve({ id: 99, ad: "Kablosuz Kulaklık" });
            } else {
                reject("Veri çekme hatası oluştu.");
            }
        }, 2000);
    });
}

// Promise Tüketimi
urunGetir()
    .then((urun) => console.log(`Ürün Geldi: ${urun.ad}`)) // urun tipi 'UrunVerisi' olarak otomatik bilinir
    .catch((hata) => console.error(hata));

2. Async/Await ile Asenkron İşlemler

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.

3. Error Handling (Hata Yönetimi - Try/Catch)

Asenkron işlemlerde internet kopması, sunucu çökmesi gibi hataları yakalayıp programın tamamen kapanmasını engellemek için try-catch blokları kurarız.

Uygulama: API Entegrasyon ve Hata Filtresi

interface HavaDurumu {
    sehir: string;
    derece: number;
}

async function havaDurumuGoster(): Promise<void> {
    try {
        console.log("Hava durumu verisi isteniyor...");
        
        // Asenkron işlemi bekliyoruz (Sanki internetten veri çekiyor gibi)
        const veri: HavaDurumu = await new Promise((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üşer
        console.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?

[]

  1. 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.

  2. 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.

  3. 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.

  4. 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?

[]

  1. 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.

  2. 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.

  3. 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.

  4. 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?

[]

  1. 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.

  2. 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.

  3. 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.

  4. 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?

[]

  1. 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.

  2. 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.

  3. 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.

  4. 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?

[]

  1. 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.

  2. 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.

  3. İç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.

  4. 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?

[]

  1. 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.

  2. 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.

  3. 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.

  4. 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?

[]

  1. Hiçbir hata vermez çünkü async fonksiyonlar dahili olarak otomatik unwrapping yapar.

    Yanlış. TypeScript derleyicisi açıkça Promise dönüşü bekler.

  2. Sadece çalışma zamanında (runtime) unhandled rejection hatası fırlatır.

    Yanlış. TypeScript statik analiz yaparak bunu derleme aşamasında yakalar.

  3. 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.

  4. 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?

[]

  1. "string" | "number"

    Yanlış. Bu property'lerin tipleridir, anahtarları (keys) değildir.

  2. name veya age nesnesinin değerlerine karşılık gelen tipler

    Yanlış. Değerleri değil, alan isimlerini (keys) union olarak alır.

  3. { name: string; age: number }

    Yanlış. Bu orijinal Person tipidir.

  4. "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?

[]

  1. T tipindeki tüm property'leri readonly yapar.

    Yanlış. Readonly yapmak için Readonly<T> utility tipi kullanılır.

  2. 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.

  3. 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.

  4. 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?

[]

  1. 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.

  2. 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.

  3. 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.

  4. 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

[]


🚀 TypeScript Mini Todo (Görev) Uygulaması Projesi

Öğ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.

1. Dosya Yapısının Kurulması (todo.ts)

// 1. Görev Durumları İçin Enum Yapısı
enum GorevDurumu {
    Beklemede = "Beklemede",
    DevamEdiyor = "Devam Ediyor",
    Tamamlandi = "Tamamlandı"
}

// 2. Görev Nesnesi İçin Interface (Sözleşme)
interface IGorev {
    readonly id: number; // ID bir kez verilir, değiştirilemez
    baslik: string;
    aciklama?: string;  // İsteğe bağlı (Opsiyonel) alan
    durum: 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ı kuruyoruz
class GorevYöneticisi<T extends IGorev> {
    // Kapsülleme (Encapsulation): Görev listesine dışarıdan doğrudan müdahale edilemez
    private gorevler: T[] = [];
    private sonrakiId: number = 1;

    // Yeni Görev Ekleme Metodu
    public gorevEkle(baslik: string, aciklama?: string): void {
        // Yeni bir görev nesnesi oluşturuyoruz (Kısmi olarak T tipine zorluyoruz)
        const yeniGorev = {
            id: this.sonrakiId++,
            baslik: baslik,
            aciklama: aciklama,
            durum: GorevDurumu.Beklemede
        } as T;

        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ü)
    public durumGuncelle(id: number, yeniDurum: GorevDurumu): void {
        const gorev = 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ı)
    public gorevleriListele(): 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) => {
            const detay = 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) üretiyoruz
const uygulamam = new GorevYöneticisi<IGorev>();

// 1. Görevleri ekleyelim
uygulamam.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örelim
uygulamam.gorevleriListele();

// 3. Bazı görevlerin durumlarını güncelleyelim
uygulamam.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ım
uygulamam.gorevleriListele();

💻 Bu Projeyi Nasıl Çalıştırırsınız?

  1. Bilgisayarınızda boş bir klasörde todo.ts dosyası açın ve yukarıdaki kodları yapıştırın.

  2. Terminalden tsc todo.ts komutunu vererek kodu JavaScript'e dönüştürün (todo.js dosyası oluşacaktır).

  3. 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.


1. "Öğrenecekleriniz" Bölümünün Açıklaması

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.


2. Giriş Kavramları

JavaScript vs TypeScript

TypeScript Kodu Kutudan Çıktığı Gibi İyileştirir mi?

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:


3. Core (Çekirdek) TypeScript Konuları ve Uygulamaları

Temel Tipleri Tanımlama (Basic Types)

TypeScript'te en temel yapılar string, number ve boolean değerleridir.

// Uygulama
let age: number = 26;
let developerName: string = "Ahmet";
let isTypeScriptAwesome: boolean = true;

Açık (Explicit) vs Örtük (Implicit) Tipler

// Uygulama
let country = "Türkiye"; // TS bunun string olduğunu otomatik anlar (Implicit)
// country = 123; // Hata verir! Çünkü o artık bir string.

getFullName Fonksiyonu Örneği

Bir fonksiyonun parametrelerini ve dönüş tipini doğru şekilde tanımlama mülakatlarda sıkça istenir.

// Uygulama
function getFullName(firstName: string, lastName: string): string {
    return `${firstName} ${lastName}`;
}

const result = getFullName("Can", "Yılmaz");

Interface (Arayüz) ve Type (Tip) Nedir? Aralarındaki Farklar Nelerdir?

Her ikisi de nesnelerin (object) şeklini tanımlamak için kullanılır.

// Interface Örneği
interface User {
    id: number;
}
interface User { // Birleştirme (Merging)
    name: string;
}

// Type Örneği
type Point = {
    x: number;
    y: number;
};
type ID = string | number; // Interface bunu yapamaz

Union (Birlik) ve Type Narrowing (Tip Daraltma)

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.

// Uygulama
function printId(id: string | number) {
    if (typeof id === "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, Never, Any ve Unknown Arasındaki Farklar

// Uygulama
function logMessage(msg: string): void {
    console.log(msg);
}

function throwError(errorMsg: string): never {
    throw new Error(errorMsg);
}

let data: unknown = "Merhaba";
// data.toUpperCase(); // HATA! Unknown doğrudan kullanılamaz.
if (typeof data === "string") {
    console.log(data.toUpperCase()); // Doğru kullanım
}

DOM ile Çalışmak ve Class Yapıları

HTML elementlerine erişirken doğru elementi cast etmek (Tiplemek) gerekir.

// DOM Uygulaması
const inputElement = document.querySelector(".user-email") as HTMLInputElement;
console.log(inputElement.value); // as HTMLInputElement sayesinde .value hata vermez

// Class Uygulaması
class Employee {
    protected id: number;
    public name: string;

    constructor(id: number, name: string) {
        this.id = id;
        this.name = name;
    }
}

Enum ve Tuple Kavramları

// Enum
enum Role {
    Admin = "ADMIN",
    User = "USER"
}

// Tuple
let product: [number, string, boolean];
product = [1, "Laptop", true]; // Sıra ve tip tam uymalıdır

Generics (Genel Tipler)

Fonksiyonların veya bileşenlerin, içine gönderilen tipe göre esnek ve yeniden kullanılabilir olmasını sağlar.

// Uygulama
function getFirstElement<T>(arr: T[]): T {
    return arr[0];
}

const numArr = [1, 2, 3];
const firstNum = getFirstElement(numArr); // TS otomatik olarak T'nin 'number' olduğunu anlar.

Utility Types (Faydalı Hazır Tipler)

Mülakatların en sevilen pratik soruları bu yardımcı tiplerden gelir:

interface Car {
    brand: string;
    model: string;
    year: number;
}

// Uygulamalar
type CarBrandAndModel = Pick<Car, "brand" | "model">; // Sadece brand ve model var
type CarWithoutYear = Omit<Car, "year">; // year hariç hepsi var
type OptionalCar = Partial<Car>; // Tüm alanlar '?' oldu

// Record Örneği (Dinamik anahtarlar)
const userRoles: Record<string, string[]> = {
    "ahmet": ["admin", "editor"],
    "mehmet": ["user"]
};

React ile TypeScript Kullanımı

React bileşenlerinde (Components) prop'ları tiplemek için interface kullanılır.

interface ButtonProps {
    label: string;
    onClick: () => void;
}

// React Component Örneği
const MyButton: React.FC<ButtonProps> = ({ label, onClick }) => {
    return <button onClick={onClick}>{label}</button>;
};

Altyapı Dosyaları: tsconfig.json, d.ts, .map ve Transpilation


4. Advanced (İleri Düzey) TypeScript Konuları ve Uygulamaları

Function Overloading (Fonksiyon Aşırı Yüklemesi)

Aynı isimde fakat farklı parametre sayıları veya tipleri alan fonksiyonlar tasarlamaktır.

// İmza (Overload Signatures)
function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
// Gerçekleşme (Implementation)
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
    if (d !== undefined && y !== undefined) {
        return new Date(y, mOrTimestamp, d);
    } else {
        return new Date(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 bulma
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

const sayHello = () => "Merhaba";
type HelloReturn = MyReturnType<typeof sayHello>; // 'string' tipini elde ederiz.

Kendin Yap (Do It Yourself) Pratikleri

Mülakatlarda sıkça sorulan "TypeScript'in kendi dahili tiplerini sıfırdan yazın" meydan okumaları: