tsconfig Temelleri: TypeScript 7 Varsayılanları ve Kaldırılan Ayarlar
tsconfig.json'daki strict ailesini, target, lib, types, module, outDir ve rootDir ayarlarını, erasableSyntaxOnly ile verbatimModuleSyntax'ı, TypeScript 6/7 varsayılanlarını ve 7.0'da kaldırılan seçenekleri görüyoruz.
Sürüm notu: Bu yazıdaki kodlar TypeScript 7.0 ve Node 26 içindir.
Önce Tahmin Et
Bir klasörde iki dosya var. Birincisi, içinde yalnızca {} yazan bir tsconfig.json; tek bir ayar bile yok. İkincisi şu discount.ts:
function applyDiscount(price, rate) {
return price - price * rate;
}
console.log(applyDiscount(100, 0.2));
Klasörde npx tsc --noEmit çalıştırıyorsun. Hiçbir ayar yazmadığına göre TypeScript bu dosyaya itiraz eder mi? Ederse neye? Cevap, TypeScript 6 ile değişen varsayılanlarda saklı.
tsconfig.json Ne İşe Yarar?
Ortam yazısında tsc --init ile bir tsconfig oluşturmuş ve npx tsc --noEmit ile tip denetimi yapmıştık. Şimdi o dosyanın içine bakma zamanı. tsconfig.json iki şey söyler: hangi dosyalar bu projeye dahil ve derleyici bu dosyalara hangi kurallarla bakacak. Dosya listesi vermezsen, tsconfig'in bulunduğu klasör ve alt klasörlerdeki bütün .ts dosyaları projeye girer.
npx tsc'yi dosya adı vermeden çalıştırdığında derleyici bulunduğun klasördeki tsconfig.json'u okur. Dosya adı verirsen ve klasörde bir tsconfig varsa, ortam yazısında gördüğümüz TS5112 hatasını alırsın. Bir de çok işe yarayan bir komut var: npx tsc --showConfig, derleyicinin tsconfig'den ne anladığını ve hangi dosyaları projeye kattığını yazdırır. Bir ayarın gerçekten etkili olup olmadığından emin değilsen ilk bakacağın yer burası.
TypeScript 6 ve 7'nin Varsayılanları
Açılıştaki sorunun cevabı: evet, itiraz eder. tsc iki hata verir:
discount.ts(1,24): error TS7006: Parameter 'price' implicitly has an 'any' type.
discount.ts(1,31): error TS7006: Parameter 'rate' implicitly has an 'any' type.
Eski bir kitapta ya da blog yazısında "TypeScript varsayılan olarak gevşektir, strict'i sen açarsın" diye okumuş olabilirsin. Bu artık doğru değil. TypeScript 6.0 varsayılanları baştan değiştirdi, 7.0 da bunları aynen korudu. Boş bir tsconfig ile bile şunlar geçerli: strict açık, module değeri esnext, target değeri es2025, types boş bir array ([]), rootDir ise tsconfig'in bulunduğu klasör. Bunlara ek olarak noUncheckedSideEffectImports da varsayılan olarak açık. 7.0, target varsayılanını "esnext'ten bir önceki ECMAScript sürümü" olarak tanımlıyor; TypeScript 7.0.2'de npx tsc --help --all bunu default: es2025 diye gösteriyor.
strict açık olduğu için noImplicitAny da açık; annotation yazılmamış ve tipi çıkarılamayan parametreler bu yüzden hata veriyor. Bu serideki bütün örnekler bu varsayılanlarla yazıldı.
tsc --init'in Ürettiği Dosya
Boş tsconfig'in de bir anlamı olduğunu gördük. npx tsc --init ise bunun üstüne önerilen ayarları ekler. TypeScript 7.0.2'nin ürettiği dosyanın yorum satırlarını çıkardığımda geriye şunlar kalıyor:
{
"compilerOptions": {
"module": "nodenext",
"target": "esnext",
"types": [],
"sourceMap": true,
"declaration": true,
"declarationMap": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"strict": true,
"jsx": "react-jsx",
"verbatimModuleSyntax": true,
"isolatedModules": true,
"noUncheckedSideEffectImports": true,
"moduleDetection": "force",
"skipLibCheck": true,
}
}
Dikkat et, bu dosya varsayılanlardan daha iddialı: module değeri nodenext, target değeri esnext. Array yazısında kısaca değindiğimiz noUncheckedIndexedAccess ve optional property'leri daha sıkı yorumlayan exactOptionalPropertyTypes da açık geliyor. sourceMap ve declaration çıktı dosyalarıyla ilgili, jsx satırı da yalnızca JSX kullanan projeleri ilgilendirir. Dosyadaki yorum satırları ayrıca rootDir, outDir ve Node için "types": ["node"] gibi sık kullanılan ayarları hazır bekletir.
strict Ailesi
strict tek bir kural değil, bir şemsiyedir. Açık olduğunda şu sekiz ayar da açılır: noImplicitAny, noImplicitThis, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, strictBuiltinIteratorReturn ve useUnknownInCatchVariables. Her birinin tsc yardımındaki varsayılanı aynıdır: "strict false değilse true". Bunlardan strictNullChecks ve noImplicitAny'yi temel tipler yazısında tanıdık; diğerleri class'lar, fonksiyon uyumluluğu ve hata yönetimi konuşulurken karşımıza çıkacak.
Ailenin tek bir üyesini kapatman gerekirse strict'i bırakıp o ayarı ayrıca yazarsın:
{
"compilerOptions": {
"strict": true,
"strictPropertyInitialization": false
}
}
Bunu ancak eski bir kod tabanını kademeli olarak taşırken yap. Yeni bir projede strict'e dokunmamak en iyisi. Bir zamanlar ailenin parçası olan alwaysStrict ise artık her zaman açık; false yapmak 7.0'da hata.
target, lib ve types
target, derleyicinin üreteceği JavaScript'in hangi ECMAScript sürümüne göre yazılacağını belirler. Aynı kaynak kodu iki farklı target ile derleyelim:
export function getPrice(price: number | null) {
return price ?? 0;
}
Varsayılan es2025 ile çıktı return price ?? 0; olarak kalır, çünkü ?? o sürümde zaten var. --target es2019 ile ise tsc aynı satırı eski motorların anlayacağı şekle çevirir:
export function getPrice(price) {
return price !== null && price !== void 0 ? price : 0;
}
Desteklenen en düşük target artık es2015; ES5 tarihe karıştı. Node 26 ve güncel tarayıcılar için varsayılan fazlasıyla yeterli.
lib, kodunun hangi hazır API'leri gördüğünü belirler. Yazmazsan target'a uygun ECMAScript tipleri ve tarayıcının dom tipleri gelir; TypeScript 6.0'dan beri dom, eskiden ayrı yazılan dom.iterable'ı da kapsıyor. lib'i elle yazarsan listeyi tamamen sen belirlersin. "lib": ["es2025"] yazıp document kullanırsan tsc ne yaptığını hemen söyler: Cannot find name 'document'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'. (TS2584).
types ise node_modules/@types altındaki paketlerle ilgili. Eskiden TypeScript bulduğu her @types paketini otomatik olarak projeye katardı; artık varsayılan [], yani hiçbirini. Node API'lerine dokunan bir dosyada process yazarsan şunu görürsün:
error TS2591: Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
Mesaj çözümü de söylüyor: npm i -D @types/node ve tsconfig'e "types": ["node"].
module ve moduleResolution
module, üretilen JavaScript'in hangi modül sistemine göre yazılacağını söyler. Varsayılan esnext, yani import ve export satırları çıktıda olduğu gibi kalır. moduleResolution ise import { x } from "./user.ts" gibi bir satırda dosyanın nasıl bulunacağını belirler. tsc yardımına göre varsayılanı, module değeri nodenext ise nodenext, node16 ya da node18 ise node16, diğer bütün durumlarda bundler.
Pratikte iki yol var. Kodun Node ile çalışacaksa "module": "nodenext" yaz; TypeScript, Node'un dosya bulma kurallarını birebir uygular. Kodun bir bundler'dan geçecekse varsayılan bundler çözümlemesi uygundur. Ayrıntıları Orta serideki modüller yazısına bırakıyoruz.
Çıktı: noEmit, outDir ve rootDir
Örneklerini node file.ts ile çalıştırıyorsan tsc'nin JavaScript üretmesine gerek yok; Node tipleri kendisi siler. Bu durumda "noEmit": true yazarsın ve tsc yalnızca tip denetimi yapar.
JavaScript üretmek istediğinde, çıktının kaynak dosyaların yanına dağılmaması için outDir kullanırsın. Burada TypeScript 6 ile gelen bir değişiklik seni şaşırtabilir. Kaynakların src klasöründeyse ve yalnızca "outDir": "dist" yazdıysan tsc şunu der:
error TS5011: The common source directory of 'tsconfig.json' is './src'. The 'rootDir' setting must be explicitly set to this or another path to adjust your output's file layout.
Visit https://aka.ms/ts6 for migration information.
Sebebi, rootDir'in artık tsconfig'in bulunduğu klasör olması. Böyle bırakırsan çıktı dist/src/main.js gibi bir yola düşerdi; tsc bunu sessizce yapmak yerine kararı sana bırakıyor. Çözüm iki satır:
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
}
}
erasableSyntaxOnly ve verbatimModuleSyntax
Bu iki ayar, kodunun yalnızca tipleri silinerek çalıştırılabilmesini güvence altına alır; node file.ts tam olarak bunu yapar. TS 5.8 ile gelen erasableSyntaxOnly, çalışma zamanında karşılığı olan TypeScript'e özgü syntax'ı yasaklar. TS 5.0 ile gelen verbatimModuleSyntax ise yalnızca type olan bir şeyi import ederken bunu import type ya da type ile açıkça belirtmeni ister; böylece hangi import'un çıktıda kalacağı tahmine kalmaz.
// user.ts dosyasında: export interface User { name: string }
// Hata: 'User' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.
import { User } from "./user.ts";
// Hata: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.
enum Role {
Admin,
Guest,
}
İlk hatanın kodu TS1484, ikincisinin TS1294. Düzeltmesi sırasıyla import type { User } from "./user.ts"; ve enum yerine başka bir yapı; enum'un neden bu kurala takıldığını Orta seride ayrıca konuşacağız. Bu seride örnekleri çalıştırmak için kullandığım tsconfig şu:
{
"compilerOptions": {
"module": "nodenext",
"noEmit": true,
"allowImportingTsExtensions": true,
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true
}
}
allowImportingTsExtensions, import'larda .ts uzantısı yazmana izin verir; Node bu uzantıyı zaten ister. nodenext ile çalışırken package.json'da "type": "module" olmasını da unutma; yoksa TypeScript dosyalarını CommonJS sayar ve import satırlarına TS1295 ile itiraz eder.
Sık Düşülen Tuzaklar: 7.0'da Kaldırılan Ayarlar
İnternetten kopyaladığın eski bir tsconfig, TypeScript 7 ile büyük ihtimalle çalışmaz. 6.0'da kullanımdan kaldırılan ayarlar 7.0'da doğrudan hataya dönüştü:
error TS5108: Option 'target=ES5' has been removed. Please remove it from your configuration.
error TS5102: Option 'baseUrl' has been removed. Please remove it from your configuration.
Use '"paths": {"*": ["./*"]}' instead.
Listenin tamamı şöyle: "target": "es5", downlevelIteration, moduleResolution için node (yani node10) ve classic, module için amd, umd, system ve none, baseUrl, outFile, esModuleInterop ya da allowSyntheticDefaultImports değerini false yapmak ve "alwaysStrict": false. TypeScript 6'da bu uyarıları "ignoreDeprecations": "6.0" ile susturabiliyordun; 7.0.2'de aynı satırı eklesen de hata yerinde kalıyor. Tek çözüm ayarı kaldırmak ya da modern karşılığına geçmek.
İkinci tuzak, node file.ts'in tsconfig'i okuduğunu sanmak. Okumaz; tsconfig yalnızca tsc'yi ve editörünü yönlendirir. Node tipleri siler ve çalıştırır, o kadar. Üçüncüsü de lib'i elle yazıp dom'u unutmak: tarayıcı kodu yazıyorsan listeye mutlaka eklemen gerekir.
Küçük Bir Deneme
★☆☆ Boş bir klasörde npx tsc --init çalıştır ve oluşan dosyayı bu yazıdaki listeyle karşılaştır. Sonra npx tsc --showConfig ile derleyicinin ne gördüğüne bak.
★★☆ Derlenir mi, tahmin et: tsconfig'e "target": "es5" ekleyip npx tsc --noEmit çalıştırıyorsun. Hata verirse hata kodu ne olur? Sonra satırı "target": "es2019" yapıp getPrice örneğini derle ve çıktıdaki ??'ya ne olduğuna bak.
★★☆ "lib": ["es2025"] yazıp document.title kullanan bir satır ekle. Hatayı oku, sonra listeye "dom" ekleyerek düzelt.
★★★ src/main.ts ve src/user.ts içeren küçük bir proje kur. Bu yazıdaki seri tsconfig'iyle tip denetimini geçir, sonra noEmit'i kaldırıp rootDir ve outDir ile çıktıyı dist klasörüne al. .ts uzantılı import'lar için hangi ek ayara ihtiyaç duyduğunu tsc'nin mesajlarından bul.
Artık derleyicinin hangi kurallarla çalıştığını biliyorsun. Bir sonraki yazıda bu bilgiyi tarayıcıya taşıyacağız: lib.dom tiplerini, querySelector'ın dönüş tiplerini, event tiplerini ve tsc ile derlediğimiz kodu bir HTML sayfasında <script type="module"> ile çalıştırmayı göreceğiz.