Mevcut bir JavaScript projesini TypeScript'e geçirmek için kodu bir seferde yeniden yazmanız gerekmez. allowJs ile .js ve .ts dosyaları aynı projede yan yana yaşar; // @ts-check yorumu ya da checkJs ayarı JavaScript dosyalarını da denetletir; JSDoc yorumlarıyla sözdizimini değiştirmeden tip eklersiniz. Sonra dosyaları birer birer .ts uzantısına çevirir ve denetim sıkılığını kademeli olarak artırırsınız. TypeScript 6.0'da strict varsayılan olarak açık geldiği için, geçişin başında "strict": false yazmak isteyebilirsiniz.

Aşağıdaki adımları küçük bir örnek proje üzerinde TypeScript 6.0.3 ile denedik ve hata mesajlarını derleyicinin verdiği haliyle aktardık. JavaScript temellerini tazelemek isterseniz JavaScript öğrenme rehberine bakabilirsiniz.

1. Adım: tsconfig.json ile başlayın

1. Adım: tsconfig.json ile başlayın

Resmî geçiş rehberi, girdi dosyalarınızı çıktıdan ayırmanızı öneriyor; aksi halde TypeScript kaynak dosyalarınızın üzerine yazabilir. Kaynak src, çıktı dist klasöründe olsun. Proje Node.js üzerinde çalışıyor ve package.json içinde "type": "module" tanımlı:

json
{
  "compilerOptions": {
    "module": "nodenext",
    "allowJs": true,
    "rootDir": "./src",
    "outDir": "./dist"
  },
  "include": ["src"]
}

Rehberdeki örnek yapılandırma "target": "es5" kullanıyor ve moduleResolution için node değerini öneriyor. TypeScript 6.0 bu iki değeri de kullanımdan kaldırdı; bu yüzden örneği güncel değerlerle yazdık. Node.js projesi için module: nodenext, paketleyici kullanan projeler için moduleResolution: bundler uygun seçimlerdir. rootDir değerini açıkça yazmanızın nedeni de 6.0'daki varsayılan değişikliğidir; ayrıntılar paths ve rootDir yazısında.

Bu noktada npx tsc çalıştırdığınızda .js dosyaları da dist klasörüne kopyalanır. TypeScript hata bulsa bile çıktıyı üretir; denememizde derleyici çıkış kodu 2 ile döndü ama dist klasörü yine oluştu. Hatalı kodun çıktıya gitmesini istemiyorsanız noEmitOnError seçeneğini açın.

2. Adım: JavaScript dosyalarını denetletin

allowJs tek başına JavaScript dosyalarındaki hataları raporlamaz. Bir dosyayı denetletmenin en kolay yolu, dosyanın ilk satırına // @ts-check eklemektir. Aşağıdaki dosyada tipler JSDoc ile verilmiş ve iki hata var:

javascript
// @ts-check

/**
 * @typedef {Object} Urun
 * @property {string} ad
 * @property {number} fiyat
 * @property {number} [adet]
 */

/**
 * @param {Urun[]} urunler
 * @returns {number}
 */
export function toplam(urunler) {
  return urunler.reduce((t, u) => t + u.fiyat * (u.adet ?? 1), 0);
}

/** @type {number} */
let indirim = 0;
indirim = "yüzde on";
// TS2322: Type 'string' is not assignable to type 'number'.

console.log(toplam([{ ad: "Kalem", fiyat: 25, adet: 2 }, { ad: "Defter", fiyat: 40 }]));
console.log(toplam([{ ad: "Silgi", fiat: 10 }]));
// TS2561: Object literal may only specify known properties, but 'fiat' does not exist in type 'Urun'. Did you mean to write 'fiyat'?

Yazım hatası olan fiat alanını derleyici yakaladı ve doğru adı da önerdi. Tüm JavaScript dosyalarını tek seferde denetletmek için "checkJs": true ayarını kullanın; dokümantasyona göre bu, her dosyaya // @ts-check eklemekle aynı etkiye sahiptir. Bazı dosyaları dışarıda bırakmak için o dosyalara // @ts-nocheck yazabilirsiniz.

Katılmadığınız tek bir hatayı susturmak için bir önceki satıra // @ts-expect-error ya da // @ts-ignore eklenir. Aradaki fark önemli: @ts-expect-error altındaki satırda hata yoksa kendisi hata üretir, böylece sorun düzeldiğinde gereksiz yorum kodda unutulmaz:

javascript
// @ts-check
/** @param {{ id: number }} m */
export function goster(m) {
  return `#${m.id}`;
}

// @ts-expect-error
console.log(goster({ id: 8 }));
// TS2578: Unused '@ts-expect-error' directive.

3. Adım: JSDoc ile tip ekleyin

3. Adım: JSDoc ile tip ekleyin: @type, @param ve @returns, @typedef, @callback

JSDoc yorumları, dosyayı .ts yapmadan önce tip bilgisi eklemenin yoludur. TypeScript @type içinde JSDoc sözdizimini de TypeScript tip sözdizimini de kabul eder; string | null ya da Array<number> gibi ifadeler çalışır. En sık kullanılan etiketler şunlardır:

EtiketKullanım
@typeBir değişkenin tipini verir: /** @type {number} */
@param ve @returnsFonksiyon parametrelerinin ve dönüş değerinin tipini verir
@typedefYeniden kullanılabilir bir nesne tipi tanımlar
@callback@typedef gibi, ama fonksiyon tipi tanımlar
@importBaşka bir dosyadaki tipi yalnızca tip denetimi için içeri alır
@templateGeneric tip parametresi tanımlar
@satisfiesDeğerin bir tipe uyduğunu, tipini değiştirmeden denetletir

Tipleri bir .ts dosyasında tutup JavaScript tarafında @import ile kullanmak, geçiş sürecinde iyi bir ara adımdır. Dokümantasyona göre bu etiket çalışma anında dosya yüklemez; içeri aldığı adlar yalnızca JSDoc yorumlarında kullanılabilir.

typescript
// tipler.ts
export interface Musteri {
  id: number;
  eposta: string;
}
javascript
// musteri.js
// @ts-check
/** @import { Musteri } from "./tipler.js" */

/**
 * @param {Musteri} m
 * @returns {string}
 */
export function etiket(m) {
  return `${m.id} <${m.eposta}>`;
}

console.log(etiket({ id: 7, eposta: "ali@ornek.com" })); // 7 <ali@ornek.com>

JavaScript dosyalarında denetim neden daha esnek?

TypeScript, .js dosyalarını eski JavaScript kalıplarıyla uyumlu olacak şekilde biraz daha hoşgörülü denetler. "Type Checking JavaScript Files" sayfasında listelenen farklardan en önemlileri şunlardır:

  • Fonksiyon parametreleri varsayılan olarak isteğe bağlıdır; eksik argümanla çağırmak hata değildir, fazla argüman ise hatadır.
  • Değişkene atanan nesne literalleri açık uçludur; sonradan ayar.port = 3000 gibi yeni alan eklemek serbesttir.
  • null, undefined ya da [] ile başlatılan değişkenler any veya any[] tipini alır.
  • require çağrıları ve module.exports atamaları CommonJS import ve export olarak tanınır.

Aşağıdaki dosyayı checkJs açıkken derledik. Eksik argüman ve sonradan eklenen alan sorun olmadı; yalnızca TypeScript 6.0'da varsayılan olarak açık olan strict nedeniyle parametreler için TS7006 hatası geldi. --strict false ile bu dosyada hiç hata kalmadı:

javascript
export function kdv(tutar, oran) {
// TS7006: Parameter 'tutar' implicitly has an 'any' type.
// TS7006: Parameter 'oran' implicitly has an 'any' type.
  return tutar * (1 + oran);
}
var ayar = {};
ayar.port = 3000;
console.log(kdv(100));

4. Adım: Dosyaları .ts uzantısına çevirin

Rehber geçişin bir sonraki adımını basitçe tarif ediyor: bir .js dosyasının adını .ts yapın, JSX içeriyorsa .tsx. Aynı dosyayı .ts olarak derlediğimizde JavaScript'teki esneklikler kalktı ve dört hata çıktı:

typescript
export function kdv(tutar, oran) {
// TS7006: Parameter 'tutar' implicitly has an 'any' type.
// TS7006: Parameter 'oran' implicitly has an 'any' type.
  return tutar * (1 + oran);
}
var ayar = {};
ayar.port = 3000;
// TS2339: Property 'port' does not exist on type '{}'.
console.log(kdv(100));
// TS2554: Expected 2 arguments, but got 1.

Bu hataların çoğu gerçek hatalardır, bazıları ise TypeScript'e niyetinizi daha iyi anlatmanız gerektiğini gösterir. Düzeltilmiş hali:

typescript
export function kdv(tutar: number, oran = 0.2): number {
  return tutar * (1 + oran);
}
const ayar = { port: 3000 };
console.log(kdv(100), ayar.port); // 120 3000

Rehberin önerileri doğrultusunda: alanları sonradan eklemek yerine nesne literalinin içinde tanımlayın ya da bir arayüz yazıp {} as Ayar biçiminde tip belirtin. require çağrılarını import, module.exports atamalarını export ifadelerine çevirin; modül tarafını JavaScript ES modülleri yazısında anlattık. Rehber, çok genel bir tip gerektiğinde Object ya da {} yerine any kullanılabileceğini söylüyor, ama any ile tip denetiminin ve editör desteğinin büyük kısmını kaybedersiniz. Bu konuda any, unknown ve never yazısına göz atın.

5. Adım: strict ayarlarını kademeli açın

5. Adım: strict ayarlarını kademeli açın: noImplicitAny, strictNullChecks, noImplicitThis, strictPropertyInitialization

TypeScript 6.0 ile strict varsayılan olarak true geliyor. Büyük bir kod tabanında ilk gün yüzlerce hata görmek istemiyorsanız "strict": false ile başlayıp alt bayrakları tek tek açabilirsiniz. Rehber, kullanmayı planladığınız sıkı ayarları mümkün olduğunca erken açmayı öneriyor ve örnek olarak noImplicitAny bayrağını dosyaları değiştirmeye başlamadan önce açmayı gösteriyor.

json
{
  "compilerOptions": {
    "strict": false,
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}
BayrakYakaladığı sorun
noImplicitAnyTipi çıkarılamadığı için sessizce any olan değişken ve parametreler (TS7006)
strictNullChecksnull veya undefined olabilecek değerlerin denetimsiz kullanımı (TS18048, TS2532)
noImplicitThisTipi belirsiz this kullanımı
strictPropertyInitializationConstructor içinde değer atanmayan sınıf alanları (TS2564)

Tüm alt bayraklar açıldığında satırı silip strict varsayılanına dönebilirsiniz. Her bayrağın ayrıntısı strict modu yazısında var.

Kütüphaneler için tip tanımları

Bir pakette tip tanımı yoksa Cannot find module türünde hatalar alırsınız. Çoğu popüler paketin tanımları DefinitelyTyped üzerinden @types paketleri olarak yayımlanır; örneğin npm install --save-dev @types/lodash. TypeScript 6.0'da types varsayılanı boş dizi oldu. Bu değişiklik import ettiğiniz paketlerin tiplerini etkilemez, ama process ya da test kütüphanelerinin describe gibi global tanımları için "types": ["node"] gibi bir liste yazmanız gerekir.

Sık yapılan hatalar

  • Çıktıyı kaynak klasörüne yazmak: outDir olmadan allowJs açarsanız TypeScript, girdi dosyasının üzerine yazacağı için TS5055 hatası verir (Cannot write file ... because it would overwrite input file.). Kaynak ve çıktıyı ayırın.
  • Tüm dosyaları bir günde çevirmeye çalışmak: Önce // @ts-check ve JSDoc ile en çok kullanılan modülleri denetletin, sonra dosya dosya ilerleyin.
  • Her hatayı any ile kapatmak: Geçiş biter ama TypeScript'in yakalayacağı hataların çoğu da gözden kaçar.
  • @ts-ignore kullanmak: Düzeltilen hatadan sonra yorum kodda kalır. @ts-expect-error bu durumu TS2578 ile size bildirir.
  • Eski rehberlerdeki değerleri kopyalamak: target: es5 ve moduleResolution: node 6.0'da kullanımdan kaldırıldı.

Sık Sorulan Sorular

JavaScript projesini TypeScript'e geçirmek için tüm dosyaları yeniden adlandırmam gerekir mi?

Hayır. allowJs açıkken iki dosya türü aynı projede çalışır. İsterseniz JSDoc ve // @ts-check ile hiç .ts dosyası yazmadan tip denetiminden yararlanabilirsiniz.

allowJs ile checkJs arasındaki fark nedir?

allowJs JavaScript dosyalarının derlemeye katılmasını ve TypeScript dosyalarından import edilmesini sağlar. checkJs ise bu dosyalardaki hataların raporlanmasını açar; tüm dosyalara // @ts-check yazmaya eşdeğerdir.

JSDoc ile yazılan tipler .ts dosyalarındaki kadar güçlü mü?

@type içinde TypeScript tip sözdiziminin büyük kısmını, koşullu tipler dahil, kullanabilirsiniz. Ancak JavaScript dosyalarında denetim bazı eski kalıplar için daha hoşgörülüdür ve bazı JSDoc etiketleri desteklenmez; desteklenenlerin listesi JSDoc Reference sayfasında.

TypeScript 6.0'da strict varsayılan olarak açık mı?

Evet. Geçişin başında çok fazla hata görüyorsanız "strict": false yazıp alt bayrakları tek tek açabilirsiniz.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: TypeScript öğrenme rehberi · JavaScript öğrenme rehberi · TypeScript nedir · TypeScript strict modu · .d.ts declaration dosyaları · sık TypeScript hataları