TypeScript Nedir? JavaScript Geliştiricisinin Tip Sistemine Gerçek Giriş
JavaScript geliştiricileri TypeScript'e genellikle iki şekilde başlar: ya zorunda kaldıkları için, ya da bir production bug'ı onları ikna ettiği için.
Her iki yolda da aynı ilk hatayla karşılaşılır: TypeScript'i JavaScript gibi yazmak, sadece arada tip eklemek. Bu yaklaşım TypeScript'in en değerli özelliklerini tamamen görmezden gelir.
TypeScript bir linter değil, bir tasarım aracıdır. Tip sistemi, kodun davranışını tanımlamanın bir yoludur — ve bunu iyi yapmak öğrenilmesi gereken ayrı bir beceridir.
Neden TypeScript?
JavaScript dinamik tiplidir. Bir değişken string olarak başlayıp number'a dönüşebilir. Bir fonksiyon beklediğiniz nesneyi döndürmeyebilir. Bu esneklik geliştirmeyi hızlandırır ama büyük kod tabanlarında bakımı çok zorlaştırır.
// JavaScript: runtime'da patlar function getUser(id) { return fetch(`/api/users/${id}`).then(r => r.json()); } const user = await getUser(123); console.log(user.naem); // undefined — typo, ama hata yok sendEmail(user.email); // email undefined olabilir, kimse uyarmadı
TypeScript bu hataları derleme zamanında yakalar:
interface User { id: number; name: string; email: string; } async function getUser(id: number): Promise<User> { return fetch(`/api/users/${id}`).then(r => r.json()); } const user = await getUser(123); console.log(user.naem); // Hata: Property "naem" does not exist on type "User" sendEmail(user.email); // email her zaman string, güvenli
Temel Tipler
// Primitives const name: string = "Ali"; const age: number = 25; const active: boolean = true; const nothing: null = null; const notDefined: undefined = undefined; // Arrays const scores: number[] = [90, 85, 92]; const names: Array<string> = ["Ali", "Veli"]; // Tuple: sabit uzunlukta, sabit tipli dizi const point: [number, number] = [10, 20]; const entry: [string, number] = ["age", 25]; // Object const user: { name: string; age: number } = { name: "Ali", age: 25 }; // Any: tip sistemini devre dışı bırakır — kaçının let data: any = "hello"; data = 42; // hata yok data.foo.bar; // hata yok — runtime'da patlayabilir // Unknown: any'nin güvenli alternatifi let input: unknown = getUserInput(); if (typeof input === "string") { input.toUpperCase(); // güvenli, tip daraltıldı }
any kullanmak TypeScript'i kapatmaktır. Gerçekten bilinmeyen tipler için unknown kullanın — onu kullanmadan önce tip kontrolü yapmak zorundasınız.
Interface ve Type: Ne Zaman Hangisi?
interface ve type çoğu durumda birbirinin yerine kullanılabilir. Ama farkları var.
// Interface: nesne şekillerini tanımlar, genişletilebilir interface Animal { name: string; age: number; } interface Dog extends Animal { breed: string; } // Declaration merging: aynı isimli iki interface birleşir interface Window { myCustomProperty: string; } // Type: daha esnek, union ve intersection için güçlü type ID = string | number; type Status = "pending" | "active" | "cancelled"; type AdminUser = User & { adminLevel: number }; // Tip takma adı olarak type Callback = (error: Error | null, result: string) => void;
Pratik kural: nesne şekilleri için interface, union tipler ve karmaşık tip ifadeleri için type tercih edin.
Union ve Intersection Tipler
// Union: A veya B type StringOrNumber = string | number; type Status = "success" | "error" | "loading"; function formatId(id: string | number): string { if (typeof id === "number") { return id.toString().padStart(6, "0"); } return id.toUpperCase(); } // Intersection: A ve B birlikte type Employee = Person & { employeeId: string; department: string }; // Discriminated Union: güçlü pattern type ApiResponse = | { status: "success"; data: User } | { status: "error"; message: string } | { status: "loading" }; function handleResponse(response: ApiResponse) { switch (response.status) { case "success": console.log(response.data.name); // data var, TypeScript biliyor break; case "error": console.log(response.message); // message var, TypeScript biliyor break; case "loading": console.log("Yukleniyor..."); break; } }
Discriminated union, TypeScript'in en güçlü pattern'larından biridir. Her case'de ne olduğunu derleyici bilir.
Generics: Yeniden Kullanılabilir Tipler
Generic'ler, tip parametreleri alan fonksiyon ve sınıflar yazmanızı sağlar. Aynı mantığı farklı tipler için tekrar yazmak zorunda kalmazsınız.
// Generic olmadan: her tip için ayrı fonksiyon function getFirstString(arr: string[]): string { return arr[0]; } function getFirstNumber(arr: number[]): number { return arr[0]; } // Generic ile: tek fonksiyon, tüm tipler için function getFirst<T>(arr: T[]): T { return arr[0]; } getFirst<string>(["a", "b", "c"]); // string getFirst<number>([1, 2, 3]); // number getFirst([true, false]); // boolean — tip çıkarımı ile otomatik // Generic interface interface ApiResponse<T> { data: T; status: number; message: string; } type UserResponse = ApiResponse<User>; type PostsResponse = ApiResponse<Post[]>; // Generic constraint: T'nin belirli bir şekli olması gerekiyor function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user = { name: "Ali", age: 25 }; getProperty(user, "name"); // string getProperty(user, "age"); // number getProperty(user, "email"); // Hata: "email" does not exist on type
Utility Types: Hazır Tip Dönüşümleri
TypeScript, sık kullanılan tip dönüşümleri için built-in utility types sunar.
interface User { id: number; name: string; email: string; password: string; createdAt: Date; } // Partial: tüm alanları opsiyonel yapar type UserUpdate = Partial<User>; // { id?: number; name?: string; email?: string; ... } // Required: tüm alanları zorunlu yapar type RequiredUser = Required<User>; // Pick: belirli alanları seçer type UserPreview = Pick<User, "id" | "name">; // { id: number; name: string } // Omit: belirli alanları çıkarır type PublicUser = Omit<User, "password">; // { id: number; name: string; email: string; createdAt: Date } // Readonly: tüm alanları salt okunur yapar type ImmutableUser = Readonly<User>; // Record: key-value map tipi type UserRoles = Record<string, "admin" | "editor" | "viewer">; // { [key: string]: "admin" | "editor" | "viewer" } // ReturnType: fonksiyonun dönüş tipini çıkarır function createUser(name: string, email: string) { return { id: Math.random(), name, email, createdAt: new Date() }; } type CreatedUser = ReturnType<typeof createUser>;
Utility types, aynı interface'i farklı bağlamlarda farklı şekillerde kullanmanızı sağlar. Omit<User, "password"> ile API response'undan şifreyi çıkarırsınız, Partial<User> ile PATCH endpoint'i için opsiyonel güncelleme tipi oluşturursunuz.
TypeScript'i Gerçekten Kullanmak
TypeScript'e geçişin en büyük tuzağı: tip sistemini "geçiştirmek". any koyarak derleyiciyi susturmak, tip hatalarını as unknown as X ile bypass etmek, her yere explicit tip yazmak yerine tip çıkarımını kullanmamak.
TypeScript'in değeri hata yakalamaktan gelir. Bu değeri almak için tip sistemini ciddiye almak gerekir. strict: true ile başlayın — tüm katı kontrolleri açar. Başlangıçta zorlayıcı gelir, uzun vadede yazdığı her kod için karşılığını verir.