JavaScript projesini TypeScript'e taşımak için gerçekçi bir yol haritası
TypeScript'e geçişin en kötü yolu, bir hafta sonu oturup bütün dosyaların uzantısını değiştirmektir. Pazartesi sabahı ekranda 1.400 hata olur, yarısını any ile susturursunuz, proje eskisinden daha güvenilmez hâle gelir. İyi haber şu: TypeScript tam olarak kademeli geçiş için tasarlandı. JavaScript ve TypeScript dosyaları aynı projede yıllarca yan yana yaşayabilir.
Aşağıdaki yol haritası, çalışan bir projeyi durdurmadan taşımak için izlediğim sıra. Her adım ayrı bir değişiklik olarak birleştirilebilir ve her adımın sonunda proje hâlâ çalışır.
Önce 2026'daki tabloya bakın
Geçişe başlamadan bilmeniz gereken birkaç güncel değişiklik var, çünkü eski yazılardaki tsconfig örnekleri artık farklı davranıyor.
TypeScript 6.0 (Mart 2026) bazı varsayılanları değiştirdi: strict artık varsayılan olarak açık, types varsayılan olarak boş dizi, yani node_modules/@types altındaki paketler otomatik yüklenmiyor. moduleResolution: "node" ve baseUrl gibi eski ayarlar kullanımdan kaldırıldı. Temmuz 2026'da çıkan TypeScript 7.0 ise derleyicinin Go ile yeniden yazılmış hâli. Microsoft'un açıkladığı ölçümlerde büyük projelerde yaklaşık 10 kat hızlı. Paket adı yine typescript, komut yine tsc. Eski ayarlara ihtiyacınız varsa 6.x sürümü @typescript/typescript6 paketiyle (tsc6 komutu) yan yana kurulabiliyor.
Geçiş açısından iki sonucu var. Birincisi, allowJs ile başlarken strict: false ayarını açıkça yazmanız gerekiyor, yoksa ilk günden tam katı modda başlarsınız. İkincisi, @types/node gibi tip paketlerini types dizisine kendiniz eklemeniz gerekiyor. 7.0 ayrıca JSDoc yorumlarını daha katı yorumluyor: @enum artık özel olarak tanınmıyor, @class bir fonksiyonu kurucuya çevirmiyor. JavaScript dosyalarında JSDoc ile ağır tip yazmayı planlıyorsanız bunu hesaba katın.
Adım adım geçiş
Aşağıdaki beş adımın her biri ayrı bir değişiklik. Bir adım bitmeden sonrakine geçilmiyor.
1. Adım: tsconfig ekleyin, hiçbir dosyayı değiştirmeyin
İlk değişiklikte tek bir .js dosyasına dokunulmuyor. Yalnızca TypeScript kurulup JavaScript'i de kabul eden gevşek bir yapılandırma ekleniyor:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"allowJs": true,
"checkJs": false,
"strict": false,
"noEmit": true,
"skipLibCheck": true,
"types": ["node"]
},
"include": ["src"]
}
noEmit şunun için var: Vite, Next.js ya da esbuild kullanıyorsanız dosyaları zaten onlar dönüştürüyor, TypeScript'in işi yalnızca kontrol etmek. Vite kullanıyorsanız types dizisine "vite/client" eklersiniz, Node ortamında çalışan kod için "node".
Sonra package.json içine bir kontrol betiği eklenip derlemeye bağlanıyor. Portföyümdeki Inkway ve Shadow Lock oyunlarında derleme betiği şöyle: "build": "tsc --noEmit && vite build". Bu önemli, çünkü Vite ve esbuild tip hatalarına bakmadan tipleri silip geçer. Tip kontrolü derlemenin ya da CI'ın parçası değilse, hatalar sessizce birikir.
2. Adım: dosyaları yapraktan köke çevirin
Dönüştürme sırası işin yarısı. Hiçbir şeyi içe aktarmayan dosyalardan başlayın: yardımcı fonksiyonlar, sabitler, formatlama, hesaplama. Sonra onları kullananlara, en son sayfalara ve giriş noktasına geçin. Bu sırayla gidince her yeni .ts dosyası, tipleri zaten belli olan dosyalardan beslenir ve hata sayısı küçük kalır.
Birkaç pratik not:
- Uzantıyı
git mvile değiştirin, dosya geçmişi korunur. - Aynı değişikliğe davranış değişikliği karıştırmayın. "Tipleri ekledim" değişikliği yalnızca tip eklemeli. Bir hata bulursanız not alın, ayrı düzeltin.
- Her seferinde birkaç dosya. İnceleyen kişi (ya da bir hafta sonraki siz) farkı okuyabilmeli.
İlk dönüştürdüğünüz dosyalarda en çok zaman alan şey, fonksiyon parametrelerinin ve dönen nesnelerin şeklini tanımlamak olur. Burada acele etmeyin, çünkü bu tipler projenin geri kalanında yeniden kullanılacak.
3. Adım: dış dünyanın tiplerini tanımlayın
Kodun kendi içi genelde kolaydır. Zorluk sınırlarda: kütüphaneler, API yanıtları, form verisi, localStorage.
Kütüphaneler için önce paketin kendi tipi var mı bakın, yoksa @types/paket-adi kurun. İkisi de yoksa bir .d.ts dosyasında en azından kullandığınız kısmı tanımlayın:
// src/types/eski-kutuphane.d.ts
declare module "eski-kutuphane" {
export function formatla(deger: number, para: string): string;
}
API yanıtları ve form verisi için daha dikkatli olmak gerekiyor. TypeScript derleme anında çalışır, çalışma anında gelen veriyi kontrol etmez. const user = await res.json() as User yazmak bir tip değil, bir umuttur. Sınırda veriyi gerçekten doğrulayan bir şema kullanmak daha sağlam. Portföyümdeki Next.js projelerinde (klinik yönetim paneli, kahve e-ticaret sitesi) form verisi sunucu tarafında Zod ile doğrulanıyor. E-ticaret sitesinde ödeme formunun tipi de şemadan türetiliyor: type CheckoutInput = z.infer<typeof checkoutSchema>. Böylece tip ve doğrulama birbirinden kopmuyor.
4. Adım: strict'i bayrak bayrak açın
Tüm dosyalar .ts olduktan sonra strict: true yapıp bir anda yüzlerce hatayla karşılaşmak yerine, bayraklar tek tek açılıyor. Her bayrak ayrı bir değişiklik:
noImplicitAny: Tipi belirtilmemiş parametreleri gösterir. Genelde en kolayı.strictNullChecks: En çok hata çıkaran ve en çok gerçek hata yakalayan bayrak.undefinedolabilecek her değeri görünür kılar.strictFunctionTypes,strictBindCallApply,useUnknownInCatchVariablesve diğerleri: Genelde az sayıda hata çıkarır.- Hepsi temizlenince tek tek bayrakları silin, yerine
"strict": trueyazın.
Hedef yapılandırma olarak Inkway'in tsconfig'ini örnek verilebilir: strict, noUnusedLocals ve noUnusedParameters açık. Portföyümdeki React Native projelerinin hepsi de strict modda. Yeni projede strict'i kapatmak için bir sebep görmüyorum. Geçiş projesinde ise strict bir varış noktası, başlangıç noktası değil.
5. Adım: any temizliği
Geçiş sırasında any kullanmak ayıp değil, bazen tek pratik yol o. Sorun any'nin kalıcı olması. Temizlik için yaptıklarım:
- Yeni
any'yi ESLint'in@typescript-eslint/no-explicit-anykuralıyla önce uyarı, geçiş bitince hata yapıyorum. - Tipini gerçekten bilmediğiniz değer için
anyyerineunknownkullanın.unknownkullanmadan önce sizi kontrol etmeye zorlar. - Susturmanız gereken bir satır varsa
@ts-ignoreyerine@ts-expect-erroryazın. Altındaki hata düzeldiğinde bu yorum kendisi hata verir ve unutulmuş susturmalar birikmez. - Projedeki
anysayısını haftada bir sayıp not ediyorum. Sayı düşüyorsa doğru yöndesiniz.
Sık yapılan hatalar
Geçişlerde en çok gördüğüm hatalar şunlar:
asile susturmak.asderleyiciye "bana güven" demektir. Ne kadar çokasvarsa tip sistemi o kadar az şey söyler.- Her yere
!koymak.user!.nameyazmakstrictNullChecks'i açıp sonra kapatmanın kısa yolu. - Çalışma anı doğrulamasını tiple karıştırmak. Tip, dışarıdan gelen veriyi kontrol etmez.
- Tip kontrolünü CI'a koymamak. Derleyici çalıştırılmıyorsa tip hataları dosyada durur ama kimse görmez.
enumve değer taşıyannamespacekullanmak. Node.js artık.tsdosyalarını doğrudan çalıştırabiliyor (tip silme özelliği Node 22.18 ve 23.6'dan beri varsayılan olarak açık, 24.12 ve 25.2'de kararlı), ama yalnızca silinebilir sözdizimini destekliyor:enum, değer içerennamespaceve parametre özellikleri hata veriyor.erasableSyntaxOnlyayarı bunları derleme anında yakalar.enumyerineas constbir nesne ya da dizi yeterli. Kahve e-ticaret sitesindeki gramajlar böyle:const WEIGHTS = [250, 500, 1000] as constvetype Weight = (typeof WEIGHTS)[number].- Her şeyi tek seferde yapmaya çalışmak. Uzun ömürlü bir "typescript" dalı ana dalla sürekli çakışır ve çoğu zaman hiç birleşmez.
Kısa kontrol listesi
-
tsconfig.jsoneklendi:allowJs: true,strict: falseaçıkça yazılı,typesdizisi dolu. -
tsc --noEmitderlemenin ya da CI'ın parçası. - Dosyalar yapraktan köke, küçük değişikliklerle çevriliyor.
- Dış veriler şemayla doğrulanıyor, tip şemadan türetiliyor.
- Strict bayrakları tek tek açılıyor.
-
anysayısı takip ediliyor ve düşüyor.
Orta boy bir projede bu sıra birkaç hafta sürer, ama her gün çalışan bir projeyle ilerlersiniz. Taşınması gereken bir JavaScript projeniz varsa bana yazabilirsiniz, birlikte bir sıra çıkarabiliriz.