TypeScript 7.1 Beta: Import Attributes ve Yeni API
TypeScript 7.1 beta sürümü yayımlandı. Bu sürüm, modül içe aktarımlarında import attributes desteğini ambient modül bildirimlerine taşıyor, ECMAScript’in source phase imports önerisini destekliyor, es2026 hedefini kararlı hâle getiriyor ve derleyici üzerine araç geliştirenler için tamamen yeni, giriş noktalarına bölünmüş bir API sunuyor. Aşağıda bu değişikliklerin ne işe yaradığını ve kodunuzda nasıl karşılık bulduğunu adım adım ele alıyoruz.
Beta sürümünü denemek için:
npm install -D typescript@beta
Ambient Modüllerde Import Attributes Desteği
Import attributes, import ifadelerine iliştirilen meta verilerdir. Çalışma zamanlarına ve bundler’lara, içe aktarılan içeriğin nasıl yorumlanacağını ve ortaya çıkacak modülün şeklini bildirirler:
import myData from "./data/someData.json" with { type: "json" };
with anahtar sözcüğünden sonra gelen nesnedeki her şey bir import attribute’tür. Tarayıcılar gibi ortamlar, bir içe aktarımı yalnızca .json gibi dosya uzantısına bakarak yorumlayamaz. type: "json" yazmak, çalışma zamanına dosyanın JSON olarak ayrıştırılıp varsayılan içe aktarım üzerinden sunulması gerektiğini söyler.
type özniteliği bu davranışı özelleştirmenin fiilî yolu hâline geldi. ECMAScript tarafında, içeriği metin olarak döndüren type: "text" için bir öneri bulunuyor:
import readme from "../README.md" with { type: "text" };
console.log(readme);
Ham baytları Uint8Array olarak döndüren type: "bytes" için de ayrı bir öneri var:
import { pngToPixels, boxBlur } from "./photoUtils.js";
import photo from "./myPhoto.png" with { type: "bytes" };
const blurryPhoto = boxBlur(pngToPixels(photo));
Tarayıcılar ise CSS dosyalarının type: "css" ile CSSStyleSheet olarak içe aktarılmasına bir süredir izin veriyor:
import someTheme from "./themes/someCoolTheme.css" with { type: "css" };
document.adoptedStyleSheets.push(someTheme);
Joker Desenli Bildirimlerin Sınırı
Bu tür dosyaları TypeScript’te tanımlamak için bugüne kadar joker desenli ambient modül bildirimleri kullanılıyordu:
// You can write this in a global `.d.ts` file with no import statements.
declare module "*.css" {
const content: CSSStyleSheet;
export default content;
}
declare module "*.txt" {
const content: string;
export default content;
}
declare module "*.md" {
const content: string;
export default content;
}
declare module "*.bin" {
const content: Uint8Array;
export default content;
}
Bu yaklaşım iş görüyor ancak import attribute’ları hiç hesaba katmadığı için her ortamda doğru sonuç vermiyor. İlk bakışta tek sorun, özniteliğin yanlışlıkla unutulmasına izin vermesi gibi görünüyor:
// ❌ oops, forgot an import attribute
import readme from "../README.md";
// ✅ what we were supposed to write
import readme from "../README.md" with { type: "text" };
Bu başlı başına çözülmeye değer bir sorun. İkincisi, bildirimler tekrara düşüyor: çalışma zamanını ilgilendiren şey dosya uzantısı değil özniteliğin kendisiyken, ikisi de type: "text" kullanacak olan .txt ve .md için ayrı desen yazmak gereksiz. Asıl büyük sorun ise ortamların farklı import attribute’ları destekleyebilmesi ve özniteliğin varlığına göre farklı davranabilmesi. Örneğin bundler’lar .css içe aktarımlarında varsayılan olarak CSS Modules adlı derleme zamanı özelliğini kullanabilirken, import ifadesinde tarayıcı destekli type: "css" belirtmek bu davranışı geçersiz kılar ve dosyanın CSSStyleSheet nesnesi olarak gelmesini sağlar.
Yeni Söz Dizimi
TypeScript 7.1 bu nedenle ambient modül bildirimlerinde import attribute desteğini getiriyor. Artık aşağıdaki gibi bildirimler yazabilir, gerekli öznitelik olmadan içe aktarım yapıldığında hata alabilir ve özniteliğe göre doğru tipi elde edebilirsiniz:
declare module "*" with { type: "css" } {
const content: CSSStyleSheet;
export default content;
}
declare module "*" with { type: "text" } {
const content: string;
export default content;
}
declare module "*" with { type: "bytes" } {
const content: Uint8Array;
export default content;
}
İki noktaya dikkat etmek gerekiyor. Birincisi, bu bildirimlerle eşleşen içe aktarımlar diskteki dosyalara karşı doğrulanmıyor; ambient modül bildirimiyle eşleşen içe aktarımlardaki mevcut davranışta olduğu gibi, başarısız çözümlemeler çalışma zamanında veya bundler’ın derleme sürecinde raporlanıyor. İkincisi, bu bildirimler TypeScript’in yerleşik kütüphanelerinin parçası olarak gelmiyor. Ekip, kütüphanelerin bunları yayımladıkları paketlere dahil etmemesini öneriyor; ihtiyaç duyan son kullanıcılar kendi projelerinde tanımlamalı. Böylece global kapsamın nasıl yapılandırılacağı (örneğin lib.dom.d.ts ile @types/node arasında) kullanıcının elinde kalıyor ve ileride yerleşik bildirimlerle çakışma yaşanmıyor.
Source Phase Imports
Source phase imports, ECMAScript’te önerilen yeni bir özellik. import anahtar sözcüğünün ardından gelen yeni bir source belirteci tanımlıyor ve yalnızca varsayılan içe aktarımlarla yazılabiliyor:
import source SomeModule from "some-module";
Fikir şu: import source kullandığımızda, modülün çalışma zamanı tarafından ayrıştırılıp analiz edilmiş, ancak kendi içe aktarımları henüz yüklenmemiş hâlini temsil eden bir nesne elde ediyoruz. Yani diğer modüllere karşı doğrulanmadan, bağlanmadan ve içindeki hiçbir kod çalışmadan önceki hâli. Bu nesne tek başına “atıl” sayılır; sonunda içe aktarılması veya örneklenmesi beklenen bir AbstractModuleSource örneğidir.
WebAssembly (wasm) içe aktarımlarında bu nesne bir WebAssembly.Module örneği oluyor ve doğrudan WebAssembly.instantiate fonksiyonuna geçirilebiliyor:
import source SuperFastThing from "./super-fast-thing.wasm";
import { WASI } from "node:wasi";
const wasi = new WASI({ /*...*/ });
const fastThingInstance = await WebAssembly.instantiate(SuperFastThing, {
wasi_snapshot_preview1: wasi.wasiImport
});
wasi.start(fastThingInstance);
WebAssembly kaynak içe aktarımları Node.js’in güncel sürümlerinde hâlihazırda destekleniyor. JavaScript dosyalarının kaynak içe aktarımıyla şu an için yapılabilecek fazla bir şey yok. Önerinin ruhu, ilerleyen aşamada bir JavaScript modül kaynağının doğrudan import() çağrısına geçirilebilmesine işaret ediyor:
import source SomeModule from "./someModule.js";
const moduleInstance = await import(SomeModule);
Zaman içinde, modüllerin nasıl yüklenip örnekleneceğini özelleştirmeye yarayan ek özelliklerin gelmesi de bekleniyor; tıpkı yukarıda WebAssembly.instantiate ile yaptığımız gibi. Ancak henüz o noktada değiliz ve birçok ortam ile araç JavaScript kaynak içe aktarımlarını hâlâ desteklemiyor.
Dinamik Kaynak İçe Aktarımı ve Kimlik
Kaynak içe aktarımları yalnızca import ifadeleriyle sınırlı değil. import.source meta-özellik fonksiyonu üzerinden dinamik olarak da yapılabiliyor:
const SuperFastThing = await import.source("./super-fast-thing.wasm");
const fastThingInstance = await WebAssembly.instantiate(SuperFastThing, {
//...
});
Kaynak içe aktarımları kimliğini korur. Yani Map ve Set içinde kullanılabilir, güvenilir biçimde karşılaştırılabilirler:
// Note that `WebAssembly.instantiate` always returns a distinct object.
import source source1 from "./plugin.wasm"
import source source2 from "./plugin.wasm"
const source3 = await import.source("./plugin.wasm");
console.log(source1 === source2); // true
console.log(source2 === source3); // true
import source kullanıldığında her içe aktarılan modülün tipi, lib.esnext.d.ts içindeki yeni global tip olan AbstractModuleSource olur. Özelliği kullanmak için uygun bir --module ayarı (esnext, nodenext veya preserve) ve --lib ya da --target tarafında esnext seçeneği gerekiyor. Bu özellik Oleksandr Tarasiuk’un çalışmasıyla hayata geçti.
–target es2026 ve Yeni Kütüphane Metotları
TypeScript 7.1, es2026 değerini kararlı bir --target ve --lib seçeneği olarak destekliyor. Burada asıl anlamlı olan lib eklemeleri: önerilen özellikler ECMAScript 2026’da kararlı hâle geldikçe ilgili bildirimler ya yeni eklendi ya da esnext‘ten taşındı.
--lib es2026 ile kararlı hâle gelenler:
Math.sumPrecise()Iterator.concat()JSON.rawJSON()- Özgün metinle ayrıştırma yapan bir
JSON.parse()aşırı yüklemesi Error.isError()Map.prototype.getOrInsert()veMap.prototype.getOrInsertComputed()WeakMap.prototype.getOrInsert()veWeakMap.prototype.getOrInsertComputed()Uint8Array.prototype.toBase64(),setFromBase64(),toHex(),setFromHex()Uint8Array.fromHex()veUint8Array.fromBase64()Array.fromAsync()
Yeni hedeflerin oluşturulması ve bildirim dosyalarının büyük bölümü yine Oleksandr Tarasiuk’un katkısıyla tamamlandı.
--lib esnext tarafında ise ECMAScript’te önerilmiş birkaç kütüphane metodu yer alıyor:
Promise.allKeyedvePromise.allSettledKeyedIterator.zipveIterator.zipKeyedIterator.prototype.chunksIterator.prototype.joinIterator.prototype.includes
TypeScript 7.1 API’si
TypeScript 7.1, derleyicinin söz dizimsel ve anlamsal analizlerini kullanarak araç geliştirmeyi sağlayan yeni bir API ile geliyor. Tek parça, devasa bir API yerine her biri belirli bir amaca hizmet eden giriş noktalarına bölünmüş bir yapı tercih edilmiş:
| Giriş noktası | Amacı |
|---|---|
typescript/sync |
Derleyici seçeneklerini, tip denetleyicisini ve program API’lerini destekleyen senkron derleyici istemcisi |
typescript/async |
Senkron istemcinin Promise tabanlı eşdeğeri |
typescript/fs |
İstek kapsamlı tam ve katmanlı dosya sistemleri |
typescript/ast |
Söz dizimi ağacı (AST) düğüm tipleri ile SyntaxKind, ScriptTarget gibi enum’lar |
typescript/ast/is |
Düğüm tip koruyucuları (type guard) |
typescript/ast/factory |
create* fonksiyonları ve değişmez update* fonksiyonları |
Aşağıdaki örnek, dosya adlarının ve derleyici seçeneklerinin komut satırından açıkça verildiği basit bir derlemeyi gösteriyor. tsc‘den farklı olarak bu örnek bir tsconfig.json dosyası keşfetmez, yüklemez ve --project desteklemez:
// Compile explicit filenames using command-line compiler options.
import { API, formatDiagnosticsWithColorAndContext, type Diagnostic } from "typescript/sync";
using api = new API();
const reportErrors = (diagnostics: readonly Diagnostic[]) => {
console.log(formatDiagnosticsWithColorAndContext(diagnostics, api))
};
// Parse command-line filenames and compiler options, report any errors.
const commandLineArgs = process.argv.slice(2);
const parsedCommandLine = api.parseCommandLine(commandLineArgs);
if (parsedCommandLine.errors.length > 0) {
reportErrors(parsedCommandLine.errors);
process.exit();
}
// Create the program by resolving and parsing all files in the entry point.
using program = api.createProgram(parsedCommandLine.fileNames, parsedCommandLine.options);
// Collect and report any diagnostics from the program before emitting.
// You can customize whether any of these block reporting errors in subsequent passes if desired.
const diagnostics = [
...program.getGlobalDiagnostics(),
...program.getProgramDiagnostics(),
...program.getSyntacticDiagnostics(),
...program.getSemanticDiagnostics(),
];
if (diagnostics.length > 0) {
reportErrors(diagnostics);
}
// Emit files - this respects the `noEmitOnError` option,
// so nothing will be emitted if an earlier step reported errors.
const emitResult = program.emit();
if (emitResult.diagnostics.length > 0) {
reportErrors(emitResult.diagnostics);
}
Neden using?
Bildirimlerdeki using anahtar sözcüğü dikkat çekiyor. API ve Program örnekleri birer Disposable nesne. Bunları oluşturduğumuzda TypeScript bellek ayırıyor ve nesnelerin Go ile JavaScript çalışma zamanları arasında yaşamaya devam etmesi için kayıt tutuyor. İşimiz bittiğinde ise çöp toplayıcıya bırakırsak Go tarafında sızıntı oluşuyor. Bu yüzden iki seçenekten biri izlenmeli:
- Açık metot çağrılarıyla elle serbest bırakmak (
api.close(),program.dispose()ya daapi[Symbol.dispose]()), - veya
usingile bildirip kapsam dışına çıktıklarında otomatik serbest bırakılmalarını sağlamak.
Bir program serbest bırakıldıktan sonra yeniden kullanılamaz.
Snapshot Kavramı
Yukarıdaki örnek düz bir “toplu derleme” senaryosuydu. Oysa TypeScript ile çalışan akışların çoğu zaman içinde oluşan değişiklikler etrafında şekilleniyor. Her seferinde dünyayı baştan kurmak yerine, API dosya güncellemeleriyle (diskte ya da kullanıcı yazarken) durum geçişlerini modelleyebiliyor.
Bu modelin merkezinde snapshot kavramı var: belirli bir andaki tüm proje ve programların bir sürümü. api.createSnapshot ile sıfırdan bir snapshot oluşturulabildiği gibi, snapshot.update(/*...*/) ile önceki bir snapshot’tan yenisi türetilebiliyor. İkinci yol hem tüm bilgiyi yeniden göndermekten hem de derleyicinin her şeyi baştan kurmasından tasarruf sağlıyor:
import { ScriptTarget } from "typescript/ast";
import { createFileSystemLayer, createFileSystemWithLib } from "typescript/fs";
import { API } from "typescript/sync";
import { resolve } from "node:path";
const fileName = resolve("snapshot-example.ts");
using api = new API();
using before = api.createSnapshot({
fileSystem: createFileSystemWithLib([
[fileName, 'export const answer: number = "forty-two";'],
]),
createPrograms: [
{
rootFiles: [fileName],
compilerOptions: {
strict: true,
noEmit: true,
target: ScriptTarget.ES2022,
},
},
],
});
using beforeProgram = before.operation.createdPrograms[0];
console.log("before:", beforeProgram.getSemanticDiagnostics().length);
using after = before.update({
fileSystem: createFileSystemLayer([
[fileName, "export const answer: number = 42;"],
]),
ensurePrograms: [beforeProgram.id],
});
using afterProgram = after.getProgram(beforeProgram.id);
if (!afterProgram) {
throw new Error("The updated program was not found.");
}
console.log("after:", afterProgram.getSemanticDiagnostics().length);
console.log("before again:", beforeProgram.getSemanticDiagnostics().length);
// Outputs:
// before: 1
// after: 0
// before again: 1
Örnekte before adlı bir snapshot’tan, değişiklik sonrası after snapshot’ı türetiliyor. Snapshot’lar, snapshot değişiklikleri arasında kalıcı olan id değerlerine sahip Program (ve Project) nesneleri içerebiliyor. ensurePrograms, yeni snapshot’ta güncellemelerini izlemek istediğimiz programları belirtiyor; mevcut varsayılan davranış daha tembel olduğundan, programları güncellemeye zorlamadan update() çağrılarını üst üste yığabiliyorsunuz.
update çağrıldıktan sonra bile önceki snapshot’ın bilgilerine erişilebildiğine dikkat edin: hem beforeProgram hem afterProgram üzerinden tanılama isteyebiliyoruz. Yine de farklı snapshot’lara ait program, proje veya checker nesnelerini birbirine karıştırmamak gerekiyor. Snapshot’lar da diğer tipler gibi Disposable; elle dispose() edilmeli ya da using bildirimiyle yerel bir bağlama alınmalı. Serbest bırakılan bir Snapshot tekrar kullanılamıyor.
Dosya Sistemi API’leri
Varsayılan olarak TypeScript API’leri host dosya sistemi üzerinde çalışır; aksini söylemediğiniz sürece dosyalara diskten erişir.
| Fonksiyon | Davranış |
|---|---|
createFileSystem(files) |
Eksiksiz bir dosya sistemi tanımlar. Listelenmeyen dosyalar, açıkça bağlanmadıkça yoktur. |
createFileSystemWithLib(files) |
Eksiksiz bir dosya sistemi tanımlar ve derleyicinin varsayılan kütüphane dizinini salt okunur olarak bağlar. |
createFileSystemLayer(files) |
Seçili dosyaları geçersiz kılar; geri kalanı temel snapshot’ın dosya sistemine veya host dosya sistemine düşer. |
Önceki örnekte createFileSystemWithLib ile kısmi bir sanal dosya sistemi oluşturup yalnızca ilgilendiğimiz dosyayı belirtmiştik. Güncelleme sırasında ise yalnızca değişen dosyalar için createFileSystemLayer kullandık; lib.d.ts dahil diğer dosyalar ilk snapshot’ta kurduğumuz temel katman üzerinden çözümlenmeye devam etti.
//...
using snapshot = api.createSnapshot({
fileSystem: createFileSystemWithLib([
[
mainFile,
[
'import { greeting } from "./generated.js";',
"export const message: string = greeting;",
].join("
"),
],
[generatedFile, 'export const greeting = "Hello from memory!";'],
]),
createPrograms: [
{
rootFiles: [mainFile],
compilerOptions: {
strict: true,
module: ModuleKind.ESNext,
moduleResolution: ModuleResolutionKind.Bundler,
target: ScriptTarget.ES2022,
},
},
],
});
using program = snapshot.operation.createdPrograms[0];
//...
// Get emit without writing files to any file system.
const emitResult = program.emitToString();
Dosyaların bu katmanlar üzerinden nasıl görüleceğini denetlemenin yolları var. Örneğin removedPaths, alttaki sisteme giden istekleri engelleyebiliyor. Dosya izleme (file watching) ihtiyacı ise şu an ayrı olarak karşılanmalı: dosya oluşturma, silme ve değiştirme olaylarının snapshot.update() içindeki fileNotifications seçeneğiyle bildirilmesi gerekiyor. host dosya sisteminde olmayan değişikliklerin de ayrı bir dosya sistemi katmanıyla sağlanması gerekiyor.
Content Mappers
TypeScript uzun süredir TypeScript ve JavaScript kodu için güçlü denetim ve araç desteği sunuyor; ancak birçok geliştirici bileşenlerini özel dosya türleriyle yazıyor. Örneğin Vue.js’te tek bir dosya içinde HTML, JavaScript ve CSS bölgeleri bir arada bulunuyor. Content mappers, duyuru metninde bu ihtiyaca yanıt olarak tanıtılıyor; konunun tam ayrıntıları için duyurunun kendisine bakmanız yerinde olur.
Kaynaklar ve İleri Okuma
- Announcing TypeScript 7.1 Beta – Daniel Rosenwasser, Microsoft DevBlogs
- MDN: import… with (import attributes)
- TC39 proposal-import-text
- TC39 proposal-import-bytes
- Ambient modüllerde import attributes desteğini ekleyen PR (#63931)
- MDN: import source
- TC39 proposal-source-phase-imports
- Node.js ESM dokümantasyonu
- es2026 hedefi ve lib eklemeleri PR’ı (#63915)
- Iterator metotlarına ilişkin PR (#64096)
- TC39 proposal-await-dictionary
- Oleksandr Tarasiuk (GitHub)
- Visual Studio 2026 Insiders 3’te TypeScript 7 Beta varsayılan
- TypeScript 6.0 ve 7.0’a geçiş köprüsü







Yorum gönder