Kısa cevap: any tip denetimini tamamen kapatır ve her şeye izin verir; unknown da her değeri kabul eder ama siz tipi daraltana kadar onunla hiçbir işlem yapmanıza izin vermez; never hiç oluşmayacak bir değeri, yani hiçbir değerin olmadığı tipi ifade eder; void ise bir fonksiyonun anlamlı bir değer döndürmediğini söyler. Dışarıdan gelen ve tipini bilmediğiniz veri için any yerine unknown kullanmak, hatayı çalışma zamanından derleme zamanına taşır.

Bu dört tip özellikle yeni başlayanların sık karıştırdığı tiplerdir. Aşağıda her birinin ne işe yaradığını, nerede kendiliğinden ortaya çıktığını ve noImplicitAny ayarının TypeScript 6.0'daki durumunu örneklerle anlatıyoruz. Örnekler TypeScript 6.0.3 ile tsc --strict --target es2022 --moduleDetection force seçenekleriyle derlendi; hata kodları ve mesajları bu derleyicinin gerçek çıktısıdır. Temel primitive tipler için temel tipler yazısına bakabilirsiniz.

any: denetimi kapatan tip

any: denetimi kapatan tip

Bir değerin tipi any olduğunda onun üzerinde sözdizimsel olarak geçerli olan her şeyi yapabilirsiniz: Olmayan bir özelliğe erişmek, fonksiyon gibi çağırmak, başka tipte bir değişkene atamak. Derleyici hiçbirine itiraz etmez, çünkü handbook'un ifadesiyle any ortamı sizin daha iyi bildiğinizi varsayar. Hata ise çalışma zamanında karşınıza çıkar:

typescript
let veri: any = { x: 0 };

const n: number = veri;  // derleyici itiraz etmez
const ayar = veri.ayar;  // tipi any, değeri undefined

try {
  veri.kaydet();
} catch (e) {
  console.log((e as Error).message); // veri.kaydet is not a function
}

any'nin asıl tehlikesi yayılmasıdır. any bir değerin özelliği de any'dir, onun döndürdüğü değer de. Tek bir any değişken, ona dokunan tüm kodda tip denetimini sessizce kapatabilir. Yine de handbook, yalnızca bir satırı derleyiciye kabul ettirmek için uzun bir tip yazmak istemediğiniz durumlarda any'nin işe yarayabileceğini söyler; JavaScript projesini adım adım taşırken de geçici olarak kullanılır (JavaScript projesini TypeScript'e geçirme).

noImplicitAny ve TypeScript 6.0 varsayılanı

Tip açıklaması yazmadığınız ve derleyicinin bağlamdan tip çıkaramadığı yerlerde TypeScript tipi any kabul eder. Buna örtük any denir. noImplicitAny açıkken derleyici bu durumları hata olarak bildirir:

typescript
function uzunluk(s) {
  return s.length;
}
// TS7006: Parameter 's' implicitly has an 'any' type.

noImplicitAny, strict ailesinin bir parçasıdır. TypeScript 6.0 sürüm notlarına göre strict artık varsayılan olarak true'dur; yani yeni bir 6.0 projesinde bu kontrol siz bir şey yazmadan açıktır. Eski varsayılana güvenen projeler tsconfig'de "strict": false yazmak zorundadır. Kendi denememizde de tsc, ek bir seçenek verilmeden yukarıdaki dosya için TS7006 hatası verdi. Ayarın tamamı için TypeScript strict modu yazısına bakın.

json
{
  "compilerOptions": {
    "strict": true
  }
}

unknown: güvenli "her şey" tipi

TypeScript 3.0 ile gelen unknown, sürüm notlarında any'nin tip güvenli karşılığı olarak tanımlanır. Her değer unknown'a atanabilir. Ama unknown bir değer, tip assertion ya da daraltma olmadan yalnızca unknown ve any tipine atanabilir; özelliğine erişilemez, çağrılamaz, aritmetikte kullanılamaz:

typescript
let deger: unknown;
deger = 42;
deger = "metin";
deger = [1, 2, 3]; // hepsi geçerli

deger.toFixed();
// TS18046: 'deger' is of type 'unknown'.
const s: string = deger;
// TS2322: Type 'unknown' is not assignable to type 'string'.

unknown ile çalışmanın yolu daraltmadır. typeof, instanceof, in ya da kendi type predicate'leriniz ile tipi kanıtladığınız dalda değer artık o tipte kullanılabilir:

typescript
function acikla(deger: unknown): string {
  if (typeof deger === "string") return "metin: " + deger.toUpperCase();
  if (typeof deger === "number") return "sayı: " + deger.toFixed(1);
  if (deger instanceof Error) return "hata: " + deger.message;
  return "bilinmiyor";
}

console.log(acikla("ts")); // metin: TS
console.log(acikla(3)); // sayı: 3.0
console.log(acikla(new Error("bozuk"))); // hata: bozuk
console.log(acikla(null)); // bilinmiyor

Tüm daraltma tekniklerini narrowing ve type guard yazısında anlatıyoruz. Pratikte unknown'ın en iyi kullanıldığı yer, tipini bilmediğiniz bir şey döndüren fonksiyonlardır. JSON.parse kendi tanımında any döndürür; onu unknown döndüren bir fonksiyonla sarmak, çağıranı kontrol yapmaya zorlar:

typescript
function guvenliParse(metin: string): unknown {
  return JSON.parse(metin);
}

const sonuc = guvenliParse('{"port": 8080}');
if (typeof sonuc === "object" && sonuc !== null && "port" in sonuc) {
  console.log(sonuc.port); // 8080
}

JSON ile çalışırken JavaScript tarafındaki davranış için JSON.parse ve JSON.stringify yazısına bakabilirsiniz.

catch değişkenleri: any yerine unknown

catch değişkenleri: any yerine unknown

JavaScript'te throw ile yalnızca Error değil, her türlü değer fırlatılabilir. TypeScript uzun süre catch değişkenini any olarak tipledi. TypeScript 4.4 ile gelen useUnknownInCatchVariables bu değişkeni unknown yapar ve sürüm notlarına göre strict ailesine dahildir. TypeScript 6.0'da strict varsayılan olarak açık olduğu için şu kod hata verir:

typescript
try {
  JSON.parse("{bozuk");
} catch (err) {
  console.log(err.message);
}
// TS18046: 'err' is of type 'unknown'.

Doğrusu, değeri kullanmadan önce gerçekten bir Error olduğunu kontrol etmektir:

typescript
try {
  JSON.parse("{bozuk");
} catch (err) {
  if (err instanceof Error) {
    console.log(err.name); // SyntaxError
  } else {
    console.log("Beklenmeyen değer:", err);
  }
}

Hata yönetiminin JavaScript tarafı için try/catch ile hata yönetimi yazısına bakabilirsiniz.

never: hiç oluşmayan değer

never hiçbir zaman gözlemlenmeyen değerlerin tipidir. Üç yerde karşınıza çıkar. Birincisi, her zaman hata fırlatan ya da programı sonlandıran fonksiyonların dönüş tipidir. İkincisi, daraltma bir union'daki tüm seçenekleri elediğinde geriye kalan tiptir. Üçüncüsü, hiçbir değerin sağlayamayacağı tiplerin sonucudur, örneğin string & number:

typescript
function hata(mesaj: string): never {
  throw new Error(mesaj);
}

function tur(x: string | number) {
  if (typeof x === "string") {
    return "metin";
  } else if (typeof x === "number") {
    return "sayı";
  } else {
    return x; // x: never
  }
}

let hicbiri: never = 1;
// TS2322: Type '1' is not assignable to type 'never'.

Kural basittir: never her tipe atanabilir, ama never'a kendisi dışında hiçbir şey atanamaz. Bu kural, switch ifadelerinin tüm durumları kapsadığını derleyiciye kontrol ettirmenin temelidir; uygulamasını narrowing yazısındaki kapsamlılık kontrolü bölümünde gösteriyoruz. Union'da never kendiliğinden kaybolur: string | never yalnızca string'dir.

void: dönüş değeri olmayan fonksiyonlar

void, değer döndürmeyen fonksiyonların dönüş tipidir. Fonksiyonda hiç return yoksa ya da return bir değer içermiyorsa çıkarılan tip void'dir. JavaScript'te böyle bir fonksiyon çalışma zamanında undefined döndürür, ama TypeScript'te void ile undefined aynı tip değildir:

typescript
function bosIs() {
  return;
} // çıkarılan dönüş tipi: void

const u: undefined = bosIs();
// TS2322: Type 'void' is not assignable to type 'undefined'.

function kaydet(): void {
  return true;
}
// TS2322: Type 'boolean' is not assignable to type 'void'.

Dönüş tipi açıkça void yazılmış bir fonksiyon bildirimi değer döndüremez. Buna karşılık () => void tipine atanan bir fonksiyon değer döndürebilir ve bu değer yok sayılır. Bu ilginç kural sayesinde dizi.forEach((x) => hedef.push(x)) gibi kodlar derlenir. Ayrıntısını fonksiyon tipleri yazısında anlatıyoruz.

Dördünü yan yana görmek

Dördünü yan yana görmek: Her şey, unknown, never, void
TipHangi değerler atanabilirDeğerle ne yapılabilirTipik kullanım
anyHer şeyHer şey, denetim yokGeçici geçiş kodu, tiplenmemiş kütüphane
unknownHer şeyDaraltana kadar neredeyse hiçbir şeyDış veri, JSON, catch değişkeni
neverHiçbir şey (kendisi hariç)Her tipe atanabilirHata fırlatan fonksiyon, kapsamlılık kontrolü
voidundefinedKullanılmaması beklenirDeğer döndürmeyen fonksiyonun dönüş tipi

Union ve intersection içinde davranışları da farklıdır. TypeScript 3.0 sürüm notlarına göre union'da unknown diğer tipleri yutar (unknown | string sonucu unknown), ama any ile birleşince sonuç any olur. Intersection'da ise diğer tipler unknown'ı yutar (unknown & string sonucu string). Ayrıca keyof unknown sonucu never'dır.

Hangisini ne zaman kullanmalı?

  • Tipini bilmediğiniz bir değer geliyorsa: unknown kullanın ve kullanmadan önce daraltın.
  • Gerçekten denetimi kapatmanız gerekiyorsa: any'yi olabildiğince dar bir alanda tutun ve mümkün olan en kısa sürede gerçek tiple değiştirin.
  • Fonksiyon hiçbir zaman normal dönmüyorsa: Dönüş tipini never yazın.
  • Fonksiyon bir değer döndürmüyorsa: void kullanın; çağıran tarafın sonucu kullanmaması gerektiğini bu tip anlatır.
  • Herhangi bir fonksiyon kabul edecekseniz: Handbook, çağrıları any döndüren Function tipi yerine () => void gibi bir fonksiyon tipini daha güvenli bulur.

Sık yapılan hatalar

  • JSON verisini any olarak bırakmak: Yanlış bir alan adı derleme sırasında yakalanmaz, çalışma zamanında undefined olarak sızar.
  • unknown'ı as ile hemen bir tipe çevirmek: deger as Kullanici hiçbir şeyi kontrol etmez; daraltma ya da doğrulama yazmadan yapılan assertion, any kadar risklidir.
  • catch değişkeninde .message'a doğrudan erişmek: Strict modda TS18046 alırsınız; önce instanceof Error ile kontrol edin.
  • void ile undefined'ı aynı sanmak: void dönüşlü bir fonksiyonun sonucu undefined tipindeki bir değişkene atanamaz.
  • strict: false ile eski projeyi sessizce gevşetmek: TypeScript 6.0'a geçerken çıkan hataları susturmak için tüm strict ailesini kapatmak yerine tek tek ayarları değerlendirin.

Sık Sorulan Sorular

unknown ile any arasındaki tek fark güvenlik mi?

Pratikte evet: İkisi de her değeri kabul eder. Fark, kullanım tarafındadır. any ile her işlem serbesttir, unknown ile tipi kanıtlamadan hiçbir işlem yapamazsınız.

Bir fonksiyon hem void hem never olabilir mi?

Hayır, anlamları farklıdır. void fonksiyon normal biter ama değer vermez; never fonksiyon hiç normal bitmez, ya hata fırlatır ya da programı sonlandırır.

noImplicitAny'yi kapatmak mantıklı mı?

Yalnızca büyük bir JavaScript projesini kademeli taşırken geçici olarak. Kapalıyken tip yazmayı unuttuğunuz her parametre sessizce any olur.

object, {} ve Object farkı nedir?

Handbook'a göre object primitive olmayan her değeri ifade eder ve {} ile global Object tipinden farklıdır; handbook her zaman object'i önerir.

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 · Narrowing ve type guard · TypeScript strict modu · Fonksiyon tipleri ve overload · null, undefined ve strictNullChecks · JavaScript hata yönetimi · TypeScript ileri seviye yazısı