TypeScript, JavaScript'e derleme öncesi statik tip kontrolü ekler. Bu kontrol bazı property, argüman ve nullability hatalarını daha erken gösterebilir; ancak runtime verisini doğrulamaz ve hatasız yazılım vaat etmez. Bu rehber, faydaları ve sınırları birlikte ele alarak kademeli geçiş stratejisi sunar.
Dinamik Tiplerde Runtime Sözleşmeleri
JavaScript dinamik tipli bir dildir. Bu esneklik birçok kullanımda yararlıdır; büyüyen kod tabanlarında ise bazı sözleşme hataları ancak ilgili kod yolu çalıştığında görülebilir.
Hipotetik Hata Örnekleri
Aşağıdaki örnekler Maviona müşteri vakası değil, tip denetiminin kapsamını göstermek için oluşturulmuş senaryolardır.
1. Property adı uyumsuzluğu:
// JavaScript - type checker property adını doğrulamaz
function getUser(id) {
return fetch(`/api/users/${id}`)
.then(res => res.json());
}
// Hipotetik hata: user.name yerine user.username kullanılıyor
async function displayUser() {
const user = await getUser(1);
return user.username;
// Nesnede alan yoksa undefined döner
}
// TypeScript — derleme zamanında yakalar
interface User {
id: number;
name: string;
email: string;
}
async function getUser(id: number): Promise<User> {
const res = await fetch(`/api/users/${id}`);
return res.json();
}
async function displayUser() {
const user = await getUser(1);
return user.username;
// TS Error: Property 'username' does not exist on type 'User'.
// Did you mean 'name'?
}
TypeScript, User tipi doğruysa bu hatayı type-check sırasında gösterebilir. Hatanın production'a gitmesini engellemek için CI'da tsc --noEmit çalıştırılmalı ve type error varken deploy durdurulmalıdır.
2. API Yanıt Uyumsuzluğu:
// JavaScript — API yanıtı değişirse ne olur?
// Backend ekibi "price" alanını "amount" olarak değiştirdi
// Frontend'de hiçbir hata vermez, sadece undefined gösterir
function displayProduct(product) {
return `${product.name} - ${product.price} TL`;
// "Laptop - undefined TL" — sessizce bozulur
}
// TypeScript — API tipi ile sözleşme
interface Product {
id: string;
name: string;
amount: number; // "price" → "amount" olarak güncellendi
currency: string;
}
function displayProduct(product: Product): string {
return `${product.name} - ${product.price} TL`;
// TS Error: Property 'price' does not exist on type 'Product'.
// Type-check, güncellenmiş sözleşmeyi kullanan yerleri işaretler
}
Bu koruma, frontend tipi backend sözleşmesiyle gerçekten güncelse geçerlidir. Elle yazılan bir interface, ağdan gelen JSON'u doğrulamaz; API şemasından kod üretimi veya runtime schema validasyonu gerekir.
3. Yanlış Tip ile Fonksiyon Çağrısı:
// JavaScript — sessizce yanlış çalışır
function calculateDiscount(price, discount) {
return price * (1 - discount / 100);
}
// Geliştirici sayı yerine string gönderdi
calculateDiscount("100", "20");
// JavaScript coercion nedeniyle 80 döner; hata sessiz kalır
// TypeScript — hatalı kullanımı engeller
function calculateDiscount(price: number, discount: number): number {
return price * (1 - discount / 100);
}
calculateDiscount("100", "20");
// TS Error: Argument of type 'string' is not assignable to parameter of type 'number'.
Kanıt ve Sınırlar
TypeScript'in resmî Handbook tanımı, onu JavaScript runtime'ı üzerinde çalışan statik tip denetleyicisi olarak açıklar. Tipler derleme sonrası silinir; bu nedenle API yanıtı, form verisi, environment variable veya local storage gibi dış girdiler runtime'da ayrıca doğrulanmalıdır.
GitHub Octoverse 2025, Ağustos 2025'te TypeScript'in GitHub'da kullanım sayımına göre ilk sıraya geçtiğini bildirir. Bu, benimsenme sinyalidir; TypeScript'in belirli bir hata yüzdesini önlediğini veya her ekipte teslimatı hızlandırdığını kanıtlamaz.
TypeScript'in Temel Faydaları
1. Otomatik Tamamlama ve Dökümantasyon
TypeScript uyumlu editörler, mevcut tip bilgisine dayanarak property, parametre ve dönüş değeri için otomatik tamamlama sunabilir. Sonuç, tip tanımlarının doğruluğuna ve any kullanımına bağlıdır.
interface BlogPost {
id: number;
title: string;
slug: string;
content: string;
tags: string[];
publishedAt: Date | null;
author: {
name: string;
avatar: string;
};
}
// IDE otomatik tamamlama — post. yazdığınızda tüm alanları gösterir
function formatPost(post: BlogPost) {
// post. → id, title, slug, content, tags, publishedAt, author
// post.author. → name, avatar
return `${post.title} by ${post.author.name}`;
}
2. Güvenli Refactoring
Bir fonksiyon adını, property'yi veya tipi değiştirdiğinizde TypeScript, analiz edebildiği referanslardaki uyumsuzlukları gösterebilir. Dinamik property erişimi, any, reflection ve runtime verisi bu kapsamın dışında kalabilir.
3. Dokümantasyon Olarak Tipler
TypeScript tipleri, veri şekli ve fonksiyon imzalarını görünür kılarak yazılı dokümantasyonu tamamlayabilir; iş kuralları ve yan etkiler yine ayrıca açıklanmalıdır:
// Fonksiyonun ne aldığı ve ne döndüğü açık
type OrderStatus = "pending" | "processing" | "shipped" | "delivered" | "cancelled";
interface CreateOrderInput {
customerId: string;
items: Array<{
productId: string;
quantity: number;
unitPrice: number;
}>;
shippingAddress: Address;
paymentMethod: "credit_card" | "bank_transfer" | "cash_on_delivery";
}
interface Order extends CreateOrderInput {
id: string;
status: OrderStatus;
totalAmount: number;
createdAt: Date;
}
declare function createOrder(input: CreateOrderInput): Promise<Order>;
4. Union Types ve Discriminated Unions
Union types, bir değerin izin verilen durumlarını açıkça modellemeye yardımcı olur:
// API yanıt durumlarını açıkça modelleme örneği
type ApiResponse<T> =
| { status: "success"; data: T }
| { status: "error"; message: string; code: number }
| { status: "loading" };
function handleResponse(response: ApiResponse<User>) {
switch (response.status) {
case "success":
// TypeScript bilir: response.data User tipinde
console.log(response.data.name);
break;
case "error":
// TypeScript bilir: response.message ve response.code var
console.error(`Hata ${response.code}: ${response.message}`);
break;
case "loading":
// TypeScript bilir: sadece status var
console.log("Yükleniyor...");
break;
}
}
5. Generics ile Yeniden Kullanılabilir Kod
// Tip parametresiyle yeniden kullanılabilen sayfalama fonksiyonu
function paginate<T>(items: T[], page: number, perPage: number): {
data: T[];
total: number;
currentPage: number;
totalPages: number;
} {
if (!Number.isInteger(page) || page < 1) {
throw new RangeError("page pozitif bir tam sayı olmalı");
}
if (!Number.isInteger(perPage) || perPage < 1) {
throw new RangeError("perPage pozitif bir tam sayı olmalı");
}
const start = (page - 1) * perPage;
const data = items.slice(start, start + perPage);
return {
data,
total: items.length,
currentPage: page,
totalPages: Math.ceil(items.length / perPage),
};
}
// TypeScript tipi otomatik çıkarır
const userPage = paginate(users, 1, 10);
// userPage.data → User[]
const productPage = paginate(products, 2, 20);
// productPage.data → Product[]
JavaScript'ten TypeScript'e Geçiş Stratejisi
Aşama 1: Hazırlık
# TypeScript'i projeye ekleyin
npm install -D typescript @types/react @types/node
# tsconfig.json oluşturun
npx tsc --init
{
"compilerOptions": {
"strict": true,
"allowJs": true,
"checkJs": false,
"noEmit": true,
"esModuleInterop": true,
"moduleResolution": "bundler",
"jsx": "preserve",
"incremental": true,
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"]
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
Bu config, bundler kullanan bir web uygulaması için öğretici örnektir; framework'ün ürettiği tsconfig dosyasını körlemesine değiştirmeyin. Yeni TypeScript dosyaları için strict: true ile başlamak, sonradan biriken belirsiz tipleri azaltır. Mevcut JavaScript dosyaları allowJs ile projede kalabilir; checkJs klasör veya dosya bazında kademeli etkinleştirilebilir. Tek bir config'in uygun olmadığı büyük repolarda ayrı tsconfig projeleri kullanın.
Aşama 2: Kademeli Dönüşüm
Strateji: Yeni dosyaları .ts/.tsx olarak oluşturun, mevcut .js dosyalarını kademeli olarak dönüştürün.
Öncelik sırası:
- Paylaşılan tipler — API yanıtları, veri modelleri için tip dosyaları oluşturun
- Utility fonksiyonlar — En çok kullanılan yardımcı fonksiyonları dönüştürün
- API katmanı — fetch wrapper'ları ve API istemcisini tip güvenli yapın
- Component'ler — Yaprak component'lerden başlayarak yukarı çıkın
// types/api.ts — merkezi tip tanımları
export interface User {
id: string;
name: string;
email: string;
role: "admin" | "user" | "editor";
createdAt: string;
}
export interface PaginatedResponse<T> {
data: T[];
pagination: {
page: number;
perPage: number;
total: number;
totalPages: number;
};
}
// lib/api.ts — tip güvenli API istemcisi
export async function fetchApi<T>(
endpoint: string,
options?: RequestInit
): Promise<T> {
const res = await fetch(`${API_BASE_URL}${endpoint}`, {
headers: { "Content-Type": "application/json" },
...options,
});
if (!res.ok) {
throw new Error(`API Error: ${res.status}`);
}
return res.json();
}
// Kullanım — bu generic yalnızca statik bir varsayımdır
const users = await fetchApi<PaginatedResponse<User>>("/users?page=1");
// users.data → User[]
// users.pagination.totalPages → number
Bu fetchApi<T> örneği JSON'u runtime'da kontrol etmez. Production sınırında yanıtı önce unknown olarak ele alın; OpenAPI/JSON Schema'dan üretilen validator veya Zod benzeri bir schema ile doğruladıktan sonra uygulama tipine dönüştürün.
Aşama 3: Strict Kapsamını Tamamlama
Ana config zaten strict ise geçici istisnaları klasör bazında azaltın. Legacy nedenle strict kapalı başlandıysa, dosya yüzdesi gibi keyfî bir eşik yerine CI hatalarını sıfırladığınız kontrollü bir değişiklikle açın:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}
Strict mode şunları zorlar:
strictNullChecks:nullveundefinedkontrollerinoImplicitAny: Açık tip tanımı olmayan değişkenleri engellerstrictFunctionTypes: Fonksiyon parametre tiplerini katı kontrol eder
Araç Önerileri
| Araç | Kullanım |
|---|---|
| TypeScript uyumlu editör | Type navigation, refactor ve otomatik tamamlama |
| ESLint + typescript-eslint | TypeScript'e özel lint kuralları |
| Prettier | Otomatik kod formatlama |
| type-coverage | Tip kapsama oranını ölçme |
| Runtime schema aracı | API ve kullanıcı girdilerini runtime'da doğrulama |
TypeScript + Next.js Kullanımı
Next.js proje oluşturma akışı TypeScript seçeneği sunar ve route props, metadata ile config için tipler sağlar. Bu framework tipleri, veritabanı veya harici API verisini kendiliğinden doğrulamaz:
// app/blog/[slug]/page.tsx — Next.js 16 + TypeScript
import type { Metadata } from "next";
import { notFound } from "next/navigation";
interface Props {
params: Promise<{ slug: string }>;
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;
const post = await getPost(slug);
if (!post) return {};
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
type: "article",
publishedTime: post.publishedAt,
},
};
}
export default async function BlogPostPage({ params }: Props) {
const { slug } = await params;
const post = await getPost(slug);
if (!post) notFound();
return (
<article>
<h1>{post.title}</h1>
<time dateTime={post.publishedAt}>{formatDate(post.publishedAt)}</time>
<div>{post.content}</div>
</article>
);
}
CMS'ten HTML veya rich text render edilecekse içerik güvenilir bir parser/renderer üzerinden geçirilmeli; TypeScript tipi XSS riskini ortadan kaldırmaz.
Sonuç
TypeScript yaygın biçimde kullanılıyor, fakat her JavaScript projesi için zorunlu değildir. Uzun ömürlü, birden çok geliştiricinin çalıştığı veya karmaşık veri sözleşmeleri olan uygulamalarda statik analiz değeri genellikle daha yüksektir. Küçük ve kısa ömürlü scriptlerde migration maliyeti ayrıca değerlendirilmelidir.
Maviona'nın TypeScript kullanımındaki amaç; sözleşmeleri görünür kılmak, refactor etkisini daha erken görmek ve CI'da statik kontrol uygulamaktır. Bu tercih hatasız kod garantisi vermez; test, runtime validasyon, gözlemlenebilirlik ve kod incelemesi yine gerekir.
Kaynaklar ve Yöntem
Tip sisteminin kapsamı ve migration seçenekleri TypeScript Handbook ile resmî JavaScript'ten geçiş rehberinden 14 Temmuz 2026'da kontrol edildi. Hipotetik kod örnekleri gerçek müşteri hatası veya ölçülmüş sonuç değildir. Kaynaklar Maviona'yı değerlendirmez veya onaylamaz.
Projenizi TypeScript'e taşımak veya sıfırdan TypeScript tabanlı bir proje başlatmak istiyorsanız, Maviona ile iletişime geçin.
