---
title: TypeScript
source: 'https://academia.sh/tr/kurslar/typescript'
language: tr
updated: '2026-08-17T04:41:41+00:00'
license: 'CC BY-SA 4.0'
lessons: 23
outcomes:
    - 'Tip çıkarımını ve daraltmayı kullanarak güvenli kod yazar'
    - 'Genelleştirilmiş ve koşullu tiplerle yeniden kullanılabilir tip tanımlar'
    - 'Derleyici seçeneklerinin katılık düzeyini bilinçli seçer'
---

# TypeScript

JavaScript üzerine kurulan statik tip katmanı: tip çıkarımı, daraltma, birleşim ve kesişim, genellikler, koşullu ve eşlenmiş tipler, bildirim dosyaları ve derleyici katılık seçenekleri.

## Tip Sistemi Temelleri

Statik tip katmanının sağladığı güvence, temel tipler, kaçış kapıları, tip çıkarımı, birleşim ve kesişim tipleri ile sabit tipler.

### TypeScript'in Amacı

Source: https://academia.sh/tr/kurslar/typescript/typescriptin-amaci

Statik tip katmanının yakaladığı hata sınıfı, tiplerin derleme anında silinmesi ve tip denetimi ile çalışma zamanı doğrulaması arasındaki ayrım.

Modüller, Araçlar ve Ekosistem kursu, kaynak kodun hedef ortama uyarlanmasını ve statik
çözümleme araçlarının kural kümesiyle nasıl çalıştığını ele aldı. Bu araçların tümü ortak
bir soru sorar: programı çalıştırmadan hakkında ne söylenebilir? TypeScript, bu sorunun
en iddialı yanıtını verir — programın her ifadesinin hangi değer kümesinden değer
üretebileceğini hesaplar ve tutarsızlıkları bildirir.

Bu ders üç şeyi kurar: tip katmanının hangi hata sınıfını yakaladığı, tiplerin çalışma
zamanında ne olduğu ve tip denetiminin **neyi vaat etmediği**. Üçüncüsü, ilk ikisi kadar
önemlidir; kursun ilerleyen bölümlerinde tekrar tekrar bu ayrıma dönülecektir.

Kurs boyunca tek bir somut örnek kullanılacaktır: bir **ölçüm kaydı** modeli. Model,
tipsiz bir JavaScript nesnesinden başlar ve her konuda bir adım daha güvenli hâle gelir.

#### Sessiz Hatanın Anatomisi

Ölçüm kayıtlarının ortalamasını hesaplayan bir fonksiyonla başlayalım. JavaScript
Temelleri kursunda kurulan araçlardan fazlasını kullanmıyor:

```javascript
function ortalama(olcumler) {
  let toplam = 0;
  for (const o of olcumler) {
    toplam += o.deger;
  }
  return toplam / olcumler.length;
}

const kayitlar = [{ deger: 21.4 }, { deger: 22.1 }, { deger: "23.0" }];
console.log(ortalama(kayitlar));
```

Çalıştırıldığında çıktı `NaN` olur. Program çökmez, hata iletisi vermez, yanlış bir sayı
üretir. Nedeni Programlama Temelleri kursundaki Tip Dönüşümü dersinde kurulmuştu: üçüncü
kaydın `deger` alanı sayı değil dizgidir; `+` işleci bir tarafı dizgi görünce toplama
değil birleştirme yapar. `43.5 + "23.0"` ifadesi `"43.523.0"` dizgisini verir, bu dizginin
üçe bölümü de `NaN` olur.

Bu, mantık hatası sınıfının tipik bir örneğidir: program çalışır, hata vermez, yanlış
sonuç verir. Hatanın kaynağı ile belirtisi arasında bir mesafe vardır — kayıt belki
saatler önce bir dosyadan okunmuş, `NaN` ise bir raporun sonunda görünmüştür.

#### Tip Katmanı Ne Yapar

Aynı programı, değerlerin hangi kümeye ait olduğunu açıkça yazarak kuralım:

```typescript
type Olcum = { deger: number };

function ortalama(olcumler: Olcum[]): number {
  let toplam = 0;
  for (const o of olcumler) {
    toplam += o.deger;
  }
  return toplam / olcumler.length;
}

const kayitlar: Olcum[] = [{ deger: 21.4 }, { deger: 22.1 }, { deger: "23.0" }];
console.log(ortalama(kayitlar));
```

`: Olcum[]` ve `: number` yazımlarına **tip açıklaması** (type annotation) denir. Derleyici
bu dosyayı denetlediğinde şu tanıyı üretir:

```text
a1.ts(11,64): error TS2322: Type 'string' is not assignable to type 'number'.
```

Tanı, hatanın nerede olduğunu değil, **nereden başladığını** gösterir: `NaN`'ın oluştuğu
bölme işlemini değil, dizginin sayı beklenen yere konduğu satır ve sütunu. Hata, belirtiye
dönüşmeden önce yakalanmıştır.

Bu kurstaki tanı iletileri derleyicinin 6.0 sürümüyle üretilmiştir. İleti metni sürümden
sürüme yeniden yazılabilir; `TS2322` gibi **hata kodları** daha kararlıdır. Bir tanıyı
aratırken kod üzerinden aramak, metin üzerinden aramaktan güvenilirdir.

#### Tipler Derleme Anında Silinir

Şimdi kursun en önemli ayrımına gelelim. TypeScript kaynak kodu doğrudan çalışmaz; önce
JavaScript'e çevrilir. Bu çeviride tip açıklamalarına ne olur?

Aşağıdaki dosya `olcum.ts` adıyla kaydedilsin:

```typescript
type Olcum = { kimlik: string; deger: number };

function ortalama(olcumler: Olcum[]): number {
  let toplam: number = 0;
  for (const o of olcumler) {
    toplam += o.deger;
  }
  return toplam / olcumler.length;
}

const kayitlar: Olcum[] = [
  { kimlik: "s-01", deger: 21.4 },
  { kimlik: "s-02", deger: 22.1 },
];
console.log(ortalama(kayitlar));
```

Derleyici `tsc --target es2022 olcum.ts` komutuyla çağrıldığında yanına `olcum.js`
dosyasını yazar:

```javascript
"use strict";
function ortalama(olcumler) {
    let toplam = 0;
    for (const o of olcumler) {
        toplam += o.deger;
    }
    return toplam / olcumler.length;
}
const kayitlar = [
    { kimlik: "s-01", deger: 21.4 },
    { kimlik: "s-02", deger: 22.1 },
];
console.log(ortalama(kayitlar));
```

Çıktı, kaynağın tip açıklamaları silinmiş hâlidir. `type Olcum` bildirimi tamamen yok
olmuştur; `: Olcum[]`, `: number` yazımları kalkmıştır. Derleyicinin eklediği tek satır
`"use strict";`dir ve o da tiplerle ilgili değildir. `node olcum.js` çıktısı `21.75`tir.

Buna **tip silme** (type erasure) denir. Sonucu şudur: **tip katmanı çalışma zamanı
davranışını değiştirmez.** Aynı programın tipli ve tipsiz sürümleri, tip hatası
içermedikleri sürece bit bit aynı işi yapar. Tip sistemi bir kütüphane değil, bir **kanıt
katmanıdır**: kaynak kod üzerinde yürütülen ve çeviri bittiğinde geride kalmayan bir akıl
yürütme.

Bu ayrımın doğrudan bir sonucu, tip hatasının kendiliğinden yayını durdurmamasıdır:

```typescript
// kayit.ts
type Olcum = { kimlik: string; deger: number };
const olcum: Olcum = { kimlik: "s-01", deger: "21.4" };
```

```text
kayit.ts(3,40): error TS2322: Type 'string' is not assignable to type 'number'.
```

Bu tanıyı üreten derleme, yine de `kayit.js` dosyasını yazar ve o dosya çalıştırılabilir.
Derleyici "bu programın tipleri tutarsız" der, "bu program çalışmaz" demez. Hatanın yayını
engellemesi ayrı bir yapılandırma kararıdır ve Derleme ve Çalıştırma Akışı dersinde ele
alınacaktır.

#### Tip Denetimi Çalışma Zamanı Doğrulaması Değildir

Tip silme, sık yapılan bir yanlış beklentiyi de açıklar. Bir değerin tipini yazmak, o
değerin çalışma zamanında gerçekten o tipte olacağını **garanti etmez**. Garanti, yalnızca
derleyicinin gördüğü kod parçası için ve yalnızca oradaki bilgi doğruysa geçerlidir.

Ölçüm kaydı bir dosyadan veya ağdan geldiğinde bilgi doğru olmayabilir:

```typescript
type Olcum = { kimlik: string; deger: number };

const ham = '{"kimlik":"s-01","deger":"23.0"}';
const olcum = JSON.parse(ham) as Olcum;

console.log(typeof olcum.deger);
try {
  console.log(olcum.deger.toFixed(1));
} catch (e) {
  console.log((e as Error).message);
}
```

Bu dosya tip denetiminden **hatasız geçer**. Çalıştırıldığında ise `TypeError` oluşur ve
`catch` bloğu iletisini yazdırır:

```text
string
olcum.deger.toFixed is not a function
```

`JSON.parse` çağrısının dönüş tipi `any`dir; `as Olcum` yazımı derleyiciye "bu değeri
`Olcum` say" demektir. Derleyici bu bildirimi sorgulamaz, çünkü sorgulayacak bir aracı
yoktur: dizginin içeriği ancak program çalışırken bilinir.

Buradan iki kural çıkar ve kurs boyunca geçerlidir:

1. **Sistem sınırında doğrulama yapılır.** Dosya, ağ, kullanıcı girdisi ve veri
   tabanından gelen veri, tipi bildirilerek değil, alan alan sınanarak kabul edilir.
2. **Tip iddiası** (type assertion) **bir kanıt değil, bir borçtur.** `as` yazımı denetimi
   susturur; susturulan yerin doğruluğundan programcı sorumludur.

Sınır doğrulaması yazmanın araçları bu kursun ilerleyen derslerinde kurulacaktır:
`unknown` tipi doğru başlangıç noktasını verir, tip yüklemleri doğrulamanın sonucunu tip
sistemine geri bildirir.

#### Tip Sistemi Neyi Kanıtlar

Bir tip sistemi, programın **çalıştırılmadan** doğrulanabilen özelliklerini kanıtlar.
TypeScript'in kanıtladığı özellik kabaca şudur: her ifadenin değeri, kendisinden beklenen
kümenin içindedir. Bu kanıt, tüm bir hata sınıfını ortadan kaldırır — var olmayan bir
alana erişim, eksik argümanla çağrı, yanlış tipte atama, birleşim tiplerinde ele alınmamış
durum.

Kanıtlamadığı şeyler de aynı ölçüde nettir:

- **Değer aralığı.** `deger: number` bildirimi, sıcaklığın fizik olarak olanaklı bir
  aralıkta olduğunu söylemez.
- **Sonlanma ve doğruluk.** Bir fonksiyonun ortalamayı doğru hesapladığı tip düzeyinde
  ifade edilemez; bu, Algoritmalar kursundaki kısmi doğruluk ve sonlanma kavramlarının
  alanıdır.
- **Dış dünyanın biçimi.** Yukarıdaki örnekte görüldüğü gibi, sınırın ötesi denetlenmez.

Ayrıca TypeScript'in tip sistemi bilinçli olarak **tam güvenilir** değildir: `any`, tip
iddiaları ve birkaç dil özelliği, sistemin verdiği güvenceyi delen kaçış kapıları bırakır.
Bunun nedeni tasarım hedefidir — dil, var olan JavaScript kodunun üzerine kademeli olarak
eklenebilmelidir. Kaçış kapıları `any`, `unknown` ve `never` dersinde tek tek ele
alınacaktır.

#### Kursun Ortak Örneği

Kurs boyunca aynı model üzerinde çalışılacaktır. Başlangıç noktası, hiçbir güvence
taşımayan bu nesnedir:

```javascript
const olcum = {
  kimlik: "s-01",
  sensor: "sicaklik",
  deger: 21.4,
  birim: "C",
  zaman: 1706000000000,
};
```

Bu nesne hakkında şu an hiçbir şey bilinmiyor: `birim` alanına herhangi bir dizgi
yazılabilir, `deger` alanı dizgi olabilir, `sensor` alanı silinebilir. Her konu bu
belirsizliklerden birini kapatacaktır — önce alanların tipleri, sonra alanlar arasındaki
bağıntılar, sonra kaydın geçtiği durumlar ve en sonunda modelin proje sınırında nasıl
yayımlanacağı.

#### Özet

- Statik tip katmanı, programı çalıştırmadan her ifadenin değer kümesini hesaplar ve
  tutarsızlıkları derleme anında bildirir.
- Tanı iletileri sürümle değişebilir; `TS2322` gibi hata kodları daha kararlı bir
  başvurudur.
- Tipler derleme anında silinir: derlenmiş JavaScript çıktısı, kaynağın tip açıklamaları
  çıkarılmış hâlidir ve çalışma zamanı davranışı değişmez.
- Tip hatası, yayını kendiliğinden durdurmaz; derleyici tanı üretse de JavaScript
  dosyasını yazar.
- Tip bildirmek çalışma zamanı doğrulaması değildir; sistem sınırından gelen veri alan
  alan sınanmalıdır.
- Tip sistemi değer aralığını, algoritmanın doğruluğunu ve dış dünyanın biçimini
  kanıtlamaz.

#### Sonraki Adım

Tip katmanının ne yaptığı belli oldu; sırada bu katmanın söz dağarcığı var. Ölçüm kaydının
alanlarını yazabilmek için önce hangi temel tiplerin bulunduğunu bilmek gerekir: ilkel
tipler, dizi ve demet gösterimleri ile sabit bir değer kümesini adlandıran `enum` yapısı.
Sonraki ders bunları kurar ve modelin ilk tipli sürümünü yazar.

### Temel Tipler

Source: https://academia.sh/tr/kurslar/typescript/temel-tipler

İlkel tipler, dizi ve salt okunur dizi gösterimleri, demetler, numaralandırmalar ve nesne tipleriyle ölçüm kaydının ilk tipli sürümü.

Önceki ders tip katmanının ne yaptığını ve ne yapmadığını kurdu. Sırada bu katmanın söz
dağarcığı var: ölçüm kaydının alanlarını yazabilmek için önce hangi tiplerin bulunduğunu
bilmek gerekir.

TypeScript'in tip dağarcığı JavaScript'in değer modelinin üzerine oturur. Yeni değer türü
eklemez; var olan türleri adlandırır ve onlardan bileşik tipler kurmanın yollarını verir.
Bu ders ilkel tipleri, dizileri, demetleri ve numaralandırmaları tanıtır; sonunda ölçüm
kaydının ilk tipli sürümünü yazar.

#### İlkel Tipler

JavaScript Temelleri kursunda tanıtılan ilkel değerlerin her birinin bir tip adı vardır.
Tip adları küçük harfle yazılır:

```typescript
const kimlik: string = "s-01";
const deger: number = 21.4;
const gecerli: boolean = true;
const aciklama: string | null = null;
const buyukSayac: bigint = 9007199254740993n;

console.log(typeof kimlik, typeof deger, typeof gecerli, typeof aciklama, typeof buyukSayac);
console.log(0.1 + 0.2 === 0.3);
console.log(Number.isInteger(deger), Number.isSafeInteger(9007199254740993));
```

Çıktı:

```text
string number boolean object bigint
false
false false
```

Üç gözlem gerekir.

**`number` tek bir sayı tipidir.** Tam sayı ile gerçel sayı ayrımı yoktur; her ikisi de
IEEE 754 çift duyarlıklı gösterimle tutulur. Bilgisayarlar Nasıl Çalışır kursundaki Kayan
Noktalı Sayılar dersinde kurulan sınırlar burada aynen geçerlidir — ikinci satırdaki
`false` bunun doğrudan sonucudur. Tip sistemi bu sınırı kaldırmaz, yalnızca bir sayının
sayı olduğunu güvence altına alır.

**`typeof null` değeri `"object"`tir.** Bu, dilin taşıdığı eski bir tutarsızlıktır; tip
sistemi `null` için ayrı bir tip tanır ama çalışma zamanı davranışını değiştirmez.
Tiplerin silindiğini gösteren bir örnektir: derleyicinin gördüğü `null` tipiyle
`typeof`'un gördüğü değer aynı yerden gelir ama aynı şeyi söylemez.

**`bigint` ve `symbol` ayrı tiplerdir.** Sayı tipinin güvenli tam sayı sınırının aşıldığı
yerlerde `bigint`, benzersiz özellik anahtarları için `symbol` kullanılır. İkisi de
`number` ile karıştırılamaz; `bigint` ile `number` doğrudan toplanamaz.

`null` ve `undefined` ayrı iki tiptir ve varsayılan katı yapılandırmada başka tiplere
kendiliğinden atanamazlar. Yukarıdaki `aciklama` bildiriminde `string | null` yazılmasının
nedeni budur; yalnızca `string` yazılsaydı derleyici atamayı reddederdi. Bu davranış
`strictNullChecks` seçeneğine bağlıdır ve Derleyici Yapılandırması dersinde ayrıntılı ele
alınacaktır.

#### Diziler ve Salt Okunur Diziler

Bir dizinin tipi, eleman tipinin ardına `[]` eklenerek yazılır. Eşdeğer bir yazım da
vardır:

```typescript
const degerler: number[] = [21.4, 22.1, 23.0];
const kimlikler: Array<string> = ["s-01", "s-02"];
const salt: readonly number[] = [21.4, 22.1];

console.log(degerler.length, kimlikler.length, salt.length);
salt.push(23.0);
```

Derleyici son satır için şu tanıyı üretir:

```text
b2.ts(6,6): error TS2339: Property 'push' does not exist on type 'readonly number[]'.
```

`number[]` ile `Array<number>` aynı tiptir; ilki kısa yazımdır. `readonly number[]` ise
farklı bir tiptir: elemanları okunabilen ama değiştirilemeyen bir dizi. Değiştirme
yöntemleri (`push`, `pop`, `sort`, `splice`) bu tipte tanımlı değildir, dolayısıyla
çağrıldıklarında "böyle bir özellik yok" tanısı gelir.

Buradaki `readonly`, Programlama Temelleri kursunda geçen **değiştirilemez** (immutable)
kavramının tip düzeyindeki karşılığıdır — ancak yalnızca tip düzeyinde. Derlenmiş çıktıda
`readonly` diye bir şey kalmaz; dizi çalışma zamanında sıradan bir dizidir ve tip
denetiminden geçmeyen bir kod onu değiştirebilir. Gerçek değiştirilemezlik için
`Object.freeze` gibi çalışma zamanı araçları gerekir.

#### Demetler

**Demet** (tuple), eleman sayısı ve her konumun tipi sabit olan bir dizidir. Ölçüm
kaydında geçerli değer aralığını temsil etmek için uygundur:

```typescript
type Aralik = [alt: number, ust: number];

const gecerliAralik: Aralik = [-40, 85];
const [alt, ust] = gecerliAralik;
console.log(alt, ust);

const yanlis: Aralik = [-40, 85, 120];
```

Son satır için tanı:

```text
b3.ts(7,7): error TS2322: Type '[number, number, number]' is not assignable to type 'Aralik'.
  Source has 3 element(s) but target allows only 2.
```

`alt:` ve `ust:` adları demet elemanlarını belgeler; çalışma zamanında karşılıkları yoktur
ve dizi yine sıra numarasıyla okunur. Adların değeri okunabilirliktedir: `[number, number]`
tipi hangi sayının önce geldiğini söylemez, `[alt: number, ust: number]` söyler.

Demet ile dizinin ayrımı şudur: dizi **kaç** eleman olduğunu bilmez, hepsinin **aynı**
tipte olduğunu bilir; demet kaç eleman olduğunu ve **her birinin ayrı** tipini bilir.

#### Sabit Değer Kümeleri: `enum`

Ölçüm kaydının `birim` alanı yalnızca belirli değerleri alabilir. Böyle bir kümeyi
adlandırmanın bir yolu **numaralandırmadır** (enum):

```typescript
enum Birim {
  Celsius = "C",
  Pascal = "Pa",
  Yuzde = "%",
}

const birim: Birim = Birim.Celsius;
console.log(birim, Birim.Pascal);
```

Bu dosya `tsc --target es2022 --strict birim.ts` ile derlendiğinde üretilen JavaScript:

```javascript
"use strict";
var Birim;
(function (Birim) {
    Birim["Celsius"] = "C";
    Birim["Pascal"] = "Pa";
    Birim["Yuzde"] = "%";
})(Birim || (Birim = {}));
const birim = Birim.Celsius;
console.log(birim, Birim.Pascal);
```

Çalıştırıldığında çıktı `C Pa` olur.

Bu çıktı önceki dersin kuralına bir istisna gösterir: **numaralandırma silinmez.**
Derleyici, çalışma zamanında var olan bir nesne üretir. Tip açıklamalarının aksine `enum`
bir tip değil, hem tip hem değer üreten bir bildirimdir.

Sayısal numaralandırmada üretilen nesne daha da fazlasını yapar:

```javascript
"use strict";
var Durum;
(function (Durum) {
    Durum[Durum["Gecerli"] = 0] = "Gecerli";
    Durum[Durum["Suphe"] = 1] = "Suphe";
    Durum[Durum["Hatali"] = 2] = "Hatali";
})(Durum || (Durum = {}));
```

Bu, `Durum.Suphe` için `1`, `Durum[1]` için `"Suphe"` veren çift yönlü bir eşlemedir.
Kolaylıktır, ama bedeli vardır: her numaralandırma paket çıktısına kod ekler ve dosya tek
tek çevrildiğinde (tip bilgisi olmadan) doğru üretilemez. Bu nedenle derleyicinin, yalnızca
silinebilir sözdizime izin veren bir seçeneği bulunur: `erasableSyntaxOnly`. Bu seçenek açıkken
`enum` bildirimi reddedilir. Yapılandırma şu olsun:

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "strict": true,
    "erasableSyntaxOnly": true,
    "noEmit": true
  }
}
```

Ve `src/durum.ts` dosyasında sayısal bir numaralandırma bulunsun:

```typescript
// src/durum.ts
enum Durum {
  Gecerli,
  Suphe,
  Hatali,
}
```

Derleyici bildirimin adını işaret eden `TS1294` koduyla reddeder:

```text
src/durum.ts(2,6): error TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.
```

Seçeneğin hangi sürümden itibaren bulunduğu derleyiciye göre değişir; davranışı ise
sabittir — çalışma zamanı kodu üreten tüm TypeScript sözdizimini reddeder.

#### Birleşim Alternatifi

Aynı kısıt, çalışma zamanında hiçbir iz bırakmayan bir yolla da ifade edilebilir:

```typescript
type Birim = "C" | "Pa" | "%";

const birim: Birim = "C";
console.log(birim);

const yanlisBirim: Birim = "F";
```

Son satır için tanı:

```text
b4.ts(6,7): error TS2322: Type '"F"' is not assignable to type 'Birim'.
```

Buradaki `"C"`, dizgi değil bir **tip**tir: yalnızca `"C"` değerini içeren tek elemanlı
küme. Böyle tiplere **sabit tip** (literal type) denir ve ayrı bir derste ele alınacaktır.
Üç sabit tipin birleşimi, üç değerli bir küme tanımlar.

İki yaklaşımın karşılaştırması:

| Ölçüt | `enum` | Sabit tip birleşimi |
|---|---|---|
| Çalışma zamanı kodu | Üretir | Üretmez |
| Değere erişim | `Birim.Celsius` | `"C"` |
| Dış veriyle eşleşme | Dönüşüm gerekir | Doğrudan |
| Tüm değerleri sayma | Nesne üzerinden | Ayrı bir dizi gerekir |

Bu kurs, tip silme ilkesini bozmadığı için sabit tip birleşimini kullanacaktır.

#### Ölçüm Kaydının İlk Tipli Sürümü

Toplanan araçlarla modelin ilk tipli sürümü yazılabilir. Nesne tipi, alan adlarını ve
tiplerini süslü ayraç içinde sayar:

```typescript
type Olcum = {
  kimlik: string;
  sensor: string;
  deger: number;
  birim: "C" | "Pa" | "%";
  zaman: number;
};

const olcum: Olcum = {
  kimlik: "s-01",
  sensor: "sicaklik",
  deger: 21.4,
  birim: "C",
  zaman: 1706000000000,
};

console.log(olcum.kimlik, olcum.deger, olcum.birim);
console.log(olcum.konum);
```

Son satır için tanı:

```text
b5.ts(18,19): error TS2339: Property 'konum' does not exist on type 'Olcum'.
```

Model artık üç şeyi bilir: hangi alanların bulunduğunu, her alanın tipini ve `birim`
alanının değer kümesini. Bilmediği şeyler de görülüyor: `sensor` alanına herhangi bir
dizgi yazılabilir, `deger` alanı için bir aralık yoktur, `zaman` alanının bir zaman damgası
mı yoksa süre mi olduğu belli değildir. Bu boşluklar sonraki konularda kapatılacaktır.

#### Özet

- İlkel tipler JavaScript'in değer modelini adlandırır; `number` tek bir sayı tipidir ve
  kayan noktalı gösterimin sınırlarını taşır.
- `T[]` ile `Array<T>` aynı tiptir; `readonly T[]` değiştirme yöntemlerini içermeyen ayrı
  bir tiptir ve yalnızca derleme anında geçerlidir.
- Demet, eleman sayısı ve her konumun tipi sabit olan dizidir; eleman adları belgeleme
  amaçlıdır.
- `enum` bildirimi çalışma zamanı kodu üretir ve tip silme ilkesinin istisnasıdır; sayısal
  numaralandırmada çift yönlü eşleme de üretilir.
- Sabit tiplerin birleşimi aynı kısıtı çalışma zamanında iz bırakmadan ifade eder.

#### Sonraki Adım

Ölçüm kaydı artık tiplidir, ancak dış dünyadan gelen veri henüz bu tipe girmemiştir.
`JSON.parse` çağrısının döndürdüğü değerin tipi nedir, o değer nasıl güvenle modele
dönüştürülür? Sonraki ders, tip sisteminin iki uç noktasını — her şeye izin veren `any` ile
hiçbir şeye izin vermeyen `unknown` — ve hiçbir değeri olmayan `never` tipini ele alacak.

### any, unknown ve never

Source: https://academia.sh/tr/kurslar/typescript/any-unknown-never

Denetimi kapatan any, denetimi zorunlu kılan unknown ve hiçbir değeri olmayan never tipinin atanabilirlik kuralları ve kullanım yerleri.

Önceki ders ölçüm kaydının ilk tipli sürümünü yazdı, ancak kayıt hâlâ elde yazılmış bir
nesneydi. Gerçek veri dosyadan veya ağdan gelir ve `JSON.parse` çağrısından çıkar. O
çağrının dönüş değeri hangi tiptedir?

Yanıt, tip sisteminin iki uç noktasını tanıtır. `any` tipi "her şey olabilir, denetleme"
der; `unknown` tipi "her şey olabilir, o yüzden önce sına" der. İkisinin arasındaki fark,
kaydın güvenliğiyle ilgili en önemli kararlardan biridir. Dersin sonunda üçüncü bir uç
gelecek: hiçbir değeri olmayan `never` tipi.

#### `any`: Denetimin Kapatılması

`any`, tip denetleyicisine bir değer hakkında hiçbir şey iddia etmemesini söyler:

```typescript
const ham: any = JSON.parse('{"kimlik":"s-01","deger":"23.0"}');

try {
  console.log(ham.deger.toFixed(1));
} catch (e) {
  console.log((e as Error).message);
}
try {
  console.log(ham.olmayan.alan);
} catch (e) {
  console.log((e as Error).message);
}
const sayi: number = ham;
```

```text
ham.deger.toFixed is not a function
Cannot read properties of undefined (reading 'alan')
```

Bu dosya tip denetiminden **hatasız geçer**; çalıştırıldığında ilk iki satır hata
fırlatır. Üç satırın üçü de hatalıdır: `deger` alanı dizgidir, `olmayan` diye bir alan
yoktur ve `ham` bir sayı değildir. Derleyici hiçbirini bildirmez, çünkü `any` değerinin
hangi alanlara sahip olduğu hakkında bir iddiası yoktur.

`any`in iki yönlü davranışı vardır: `any` tipindeki bir değer her tipe atanabilir, her tip
de `any`e atanabilir. Bu, tipin denetim ağından çıkarılması demektir. Bir alan `any`
olduğunda, o alandan türeyen her ifade de denetimsiz kalır — denetimsizlik yayılır.

`JSON.parse` fonksiyonunun bildirilmiş dönüş tipi `any`dir. Bunun nedeni açık: dizginin
içeriği ancak çalışma zamanında bilinir, derleyicinin söyleyecek bir şeyi yoktur. Sonuç,
ilk derste görülen duruma benzer — tip denetimi susar, hata çalışma zamanına ertelenir.

Derleyici, tip yazılmadığında `any` çıkarmayı da reddedebilir:

```typescript
function ortalama(olcumler) {
  return olcumler.length;
}
console.log(ortalama([1, 2, 3]));
```

```text
c6.ts(1,19): error TS7006: Parameter 'olcumler' implicitly has an 'any' type.
```

Bu tanı `noImplicitAny` seçeneğinden gelir ve katı yapılandırmanın parçasıdır. Ayrımı
görmek gerekir: **örtük** `any` bir eksikliktir, derleyici uyarır. **Açık** `any` bir
karardır, derleyici susar. Kod tabanında `any` yazımlarını aramak, denetimin kapatıldığı
yerleri aramak demektir.

#### `unknown`: Denetimin Ertelenmesi

`unknown`, `any` ile aynı değer kümesini kapsar — her değer `unknown` tipine atanabilir —
ama ters yön kapalıdır:

```typescript
const ham: unknown = JSON.parse('{"kimlik":"s-01","deger":"23.0"}');

console.log(ham.deger);
const sayi: number = ham;
```

```text
c2.ts(3,13): error TS18046: 'ham' is of type 'unknown'.
c2.ts(4,7): error TS2322: Type 'unknown' is not assignable to type 'number'.
```

`unknown` tipindeki bir değerle **hiçbir şey yapılamaz**: alanına erişilemez,
çağrılamaz, başka bir tipe atanamaz. Yapılabilecek tek şey, tipini daraltmaktır.

İki tipin ilişkisi bir sıralamayla anlatılabilir. Atanabilirlik yönünde `unknown` en üstte
durur: her tip ona atanır, o hiçbir tipe atanmaz. Bu nedenle **en üst tip** (top type)
denir. `any` ise bu sıralamanın dışındadır; her iki yöne de atanır ve tam bu yüzden bir
güvence taşımaz.

Pratik kural: **sistem sınırında `unknown` kullanılır.** Dış veri `unknown` olarak alınır,
sınanır, ancak sınamadan sonra modele girer.

#### Sınırda Doğrulama

Ölçüm kaydının dosyadan okunması bu kuralın uygulanmasıdır:

```typescript
type Olcum = { kimlik: string; deger: number };

function oku(ham: string): Olcum | null {
  const cozulen: unknown = JSON.parse(ham);
  if (
    typeof cozulen === "object" &&
    cozulen !== null &&
    "kimlik" in cozulen &&
    typeof cozulen.kimlik === "string" &&
    "deger" in cozulen &&
    typeof cozulen.deger === "number"
  ) {
    return { kimlik: cozulen.kimlik, deger: cozulen.deger };
  }
  return null;
}

console.log(oku('{"kimlik":"s-01","deger":21.4}'));
console.log(oku('{"kimlik":"s-01","deger":"23.0"}'));
console.log(oku('42'));
```

Çıktı:

```text
{ kimlik: 's-01', deger: 21.4 }
null
null
```

Koşulun her parçası bir adım daraltma yapar. `typeof cozulen === "object"` ifadesinden
sonra derleyici değerin bir nesne olduğunu bilir; `cozulen !== null` ile `null` olasılığı
elenir; `"kimlik" in cozulen` ile o alanın varlığı kanıtlanır; `typeof` sınamasıyla alanın
tipi belirlenir. Koşulun içine girildiğinde derleyici `cozulen.kimlik` ifadesinin `string`
olduğunu **kanıtlamıştır** — programcının sözüne değil, koddaki sınamalara dayanarak.

Bu, kursun eksenidir: tip sistemi bir kanıt katmanıdır ve sınamalar kanıtın adımlarıdır.
Daraltmanın kuralları ile daha az tekrar eden yazım biçimleri Tip Daraltma dersinde ele
alınacaktır.

Aynı işi `as Olcum` yazarak yapmak da mümkündü ve ilk derste görüldüğü gibi hatalı
davranırdı. Fark şurada: `as` yazımında güvence programcının iddiasından, yukarıdaki
kodda ise gerçekten çalışan sınamalardan gelir.

#### `never`: Boş Tip

`never`, hiçbir değeri olmayan tiptir. Boş kümedir. İki yerde ortaya çıkar.

Birincisi, hiçbir zaman normal yoldan dönmeyen fonksiyonların dönüş tipi:

```typescript
function dur(ileti: string): never {
  throw new Error(ileti);
}

const her: string = dur("kayit okunamadi");
const hicbir: never = "s-01";
console.log(her, hicbir);
```

```text
c5.ts(6,7): error TS2322: Type '"s-01"' is not assignable to type 'never'.
```

Beşinci satır hata vermez, altıncı verir. Bunun nedeni `never`in atanabilirlik
sıralamasındaki yeridir: `never` her tipe atanabilir, hiçbir tip `never`e atanamaz — tıpkı
boş kümenin her kümenin alt kümesi olması gibi. Bu yüzden `never` **en alt tip** (bottom type)
diye anılır. `dur` fonksiyonu hiçbir değer döndürmediği için, döndürdüğü değerin `string`
olması gerektiğini söylemek boş bir iddiadır ve derleyici buna izin verir.

İkinci ortaya çıkışı daha yararlıdır: **tükenmişlik denetimi** (exhaustiveness checking).
Ölçüm kaydının durumları bir birleşimle tanımlansın:

```typescript
type Durum = "gecerli" | "suphe" | "hatali";

function etiket(durum: Durum): string {
  switch (durum) {
    case "gecerli":
      return "olcum gecerli";
    case "suphe":
      return "olcum supheli";
    default: {
      const elealinmayan: never = durum;
      return elealinmayan;
    }
  }
}

console.log(etiket("gecerli"));
```

```text
c4.ts(10,13): error TS2322: Type '"hatali"' is not assignable to type 'never'.
```

Tanı, ele alınmayan durumun adını doğrudan söyler. Mekanizma şudur: iki `case` dalından
sonra derleyici, `default` dalına yalnızca `"hatali"` değerinin ulaşabileceğini bilir.
`never` tipine atama denemesi, kalan kümenin boş olup olmadığını sınar. Küme boş değilse
tanı gelir ve içindeki değer tanının metnine yazılır.

Bu kalıbın değeri, birleşime yeni bir durum eklendiğinde ortaya çıkar. `Durum` tipine
dördüncü bir değer eklenirse, o değeri ele almayan **her** `switch` bloğu derleme anında
hata verir. Tip sistemi burada bir bakım aracına dönüşür: modelin değişmesi, modeli
kullanan tüm kodun gözden geçirilmesini zorunlu kılar.

#### Üç Tipin Yeri

Üçünün ilişkisi tek bir tabloda toplanabilir:

| Tip | Ona atanabilenler | Ona atananın gidebildiği yerler | Kullanım yeri |
|---|---|---|---|
| `any` | Her tip | Her tip | Kademeli geçişte geçici |
| `unknown` | Her tip | Yok (daraltma gerekir) | Sistem sınırı |
| `never` | Yok | Her tip | Tükenmişlik denetimi, dönmeyen fonksiyon |

Tablodaki ilk satır `any`in neden bir tip değil bir kapı olduğunu gösterir: her iki sütunu
da dolu olan tek satırdır ve bu, hiçbir kısıt getirmediği anlamına gelir. `unknown` ile
`never` ise birbirinin karşıtıdır — biri her şeyi kapsar ve hiçbir şeye izin vermez, diğeri
hiçbir şeyi kapsamaz ve her yere gider.

`any` tamamen yasaklanmaz. JavaScript ile Birlikte Çalışma dersinde görüleceği gibi, tip
bilgisi olmayan bir kod tabanına kademeli geçişte geçici olarak gereklidir. Kural şudur:
`any` bir **geçiş** işaretidir, bir **çözüm** değil; kaldığı yer belgelenir ve daraltılır.

#### Özet

- `any`, tip denetimini kapatır; her iki yönde atanabilir olduğu için hiçbir güvence
  taşımaz ve denetimsizliği yayar.
- Örtük `any` bir eksikliktir ve `noImplicitAny` seçeneğiyle bildirilir; açık `any` bir
  karardır.
- `unknown` en üst tiptir: her değer ona atanır, o hiçbir tipe atanmaz; kullanılabilmesi
  için daraltılması gerekir.
- Sistem sınırından gelen veri `unknown` olarak alınır ve alan alan sınanarak modele
  girer; `as` yazımı bu sınamanın yerine geçmez.
- `never` en alt tiptir: hiçbir değeri yoktur, her tipe atanır; `switch` bloklarında
  tükenmişlik denetimi için kullanılır.

#### Sonraki Adım

Buraya kadar tipler elle yazıldı. Oysa yukarıdaki örneklerin çoğunda derleyici, yazılmamış
tipleri kendiliğinden biliyordu: `oku` fonksiyonunun içindeki daraltma sonrası tipler,
nesne sabitinin alan tipleri, dizi elemanlarının tipi. Sonraki ders bu bilgiyi nereden
aldığını — tip çıkarımının kurallarını ve sınırlarını — ele alacak.

### Tip Çıkarımı

Source: https://academia.sh/tr/kurslar/typescript/tip-cikarimi

Derleyicinin ilk değerden, bağlamdan ve dönüş ifadelerinden tip çıkarma kuralları; genişletme davranışı ve tip açıklamasının hangi noktalarda gerekli olduğu.

Önceki derslerin örneklerinde derleyici, yazılmamış birçok tipi biliyordu: daraltma
sonrası alanların tipleri, dizi elemanlarının tipi, `reduce` geri çağrısının parametreleri.
Bu bilgiyi nereden aldı?

Yanıt **tip çıkarımıdır** (type inference): derleyici, bir ifadenin tipini çevresindeki
bilgiden hesaplar. Çıkarım TypeScript'i kullanılabilir kılan şeydir — her değişkene tip
yazmak gerekseydi tip katmanının maliyeti yararını aşardı. Bu ders çıkarımın kurallarını,
sınırlarını ve tip açıklamasının hâlâ gerektiği noktaları ele alır.

#### Çıkarılan Tipi Görmek

Çıkarımı incelemenin en doğrudan yolu, derleyiciden **bildirim dosyası** üretmesini
istemektir. Bildirim dosyası, bir modülün dışa aktardığı adların tiplerini yazar; çıkarılan
tipler orada açıkça görünür.

```typescript
export const birim = "C";
export let degisken = "C";
export const deger = 21.4;
export const olcum = { kimlik: "s-01", deger: 21.4, birim: "C" };
export const degerler = [21.4, 22.1];
export const karisik = [21.4, "yok"];

export function ortalama(olcumler: number[]) {
  return olcumler.reduce((t, d) => t + d, 0) / olcumler.length;
}
```

Dosya `cikarim.ts` adıyla kaydedilip
`tsc --strict --target es2022 --declaration --emitDeclarationOnly cikarim.ts` komutuyla
derlendiğinde yanına `cikarim.d.ts` yazılır:

```typescript
export declare const birim = "C";
export declare let degisken: string;
export declare const deger = 21.4;
export declare const olcum: {
    kimlik: string;
    deger: number;
    birim: string;
};
export declare const degerler: number[];
export declare const karisik: (string | number)[];
export declare function ortalama(olcumler: number[]): number;
```

Bu dosya, çıkarımın altı kuralını tek bakışta gösterir. Sırayla ele alalım.

#### İlk Değerden Çıkarım ve Genişletme

`birim` sabitinin tipi `string` değil `"C"`dir — yalnızca tek bir değer içeren küme. `deger`
sabitinin tipi de `number` değil `21.4`tür. Buna karşılık `degisken` bildiriminin tipi
`string`e **genişletilmiştir** (widening).

Kural şudur: değeri değişebilen bir bildirimde sabit tip tutulamaz, çünkü bildirim daha
sonra aynı tipte başka bir değer alabilir. `let degisken = "C"` bildiriminden sonra
`degisken = "Pa"` yazılabilir; tip `"C"` kalsaydı bu atama reddedilirdi. Değeri
değişemeyen bir bildirimde ise genişletmeye gerek yoktur.

Aynı gerekçe nesne alanları için de geçerlidir. `olcum` sabiti `const` ile bildirilmiş
olmasına rağmen `birim` alanının tipi `"C"` değil `string` çıkarılmıştır. Nedeni,
JavaScript Temelleri kursunda kurulan ayrımdır: `const` bildirimi **bağlamayı** sabitler,
nesnenin içeriğini değil. `olcum.birim = "Pa"` yazımı geçerli bir JavaScript ifadesidir,
dolayısıyla alanın tipi genişletilir.

Bu davranışın doğrudan bir sonucu vardır:

```typescript
type Birim = "C" | "Pa" | "%";
type Olcum = { kimlik: string; deger: number; birim: Birim };

const ham = { kimlik: "s-01", deger: 21.4, birim: "C" };
const kayit: Olcum = ham;
console.log(kayit);
```

```text
d4.ts(5,7): error TS2322: Type '{ kimlik: string; deger: number; birim: string; }' is not assignable to type 'Olcum'.
  Types of property 'birim' are incompatible.
    Type 'string' is not assignable to type 'Birim'.
```

Atama başarısız olur, çünkü `ham.birim` alanının tipi `string`e genişlemiştir ve `string`,
üç değerli `Birim` kümesine sığmaz. Genişletmeyi engellemenin yolu Sabit Tipler ve
`as const` dersinin konusudur.

Dizilerde çıkarım, elemanların tiplerinin birleşimini alır: `degerler` için `number[]`,
karışık elemanlı `karisik` için `(string | number)[]`. Dizi de değiştirilebilir olduğu
için eleman tipleri genişletilir.

#### Bağlamsal Tipleme

Çıkarım tek yönlü değildir. Bir ifadenin tipi, o ifadenin **beklendiği yerden** de
gelebilir. Buna **bağlamsal tipleme** (contextual typing) denir:

```typescript
const degerler: number[] = [21.4, 22.1, 23.0];

degerler.forEach((d) => console.log(d.toFixed(1)));
degerler.forEach((d) => console.log(d.toUpperCase()));
```

```text
d1.ts(4,39): error TS2339: Property 'toUpperCase' does not exist on type 'number'.
```

`d` parametresine tip yazılmamıştır. Derleyici tipi, `forEach` yönteminin beklediği geri
çağrı tipinden alır: `degerler` bir `number[]` olduğuna göre geri çağrının ilk parametresi
`number`dır. Üçüncü satır geçerlidir, dördüncü satır dizgi yöntemi çağırdığı için
reddedilir.

Bağlamsal tipleme, örtük `any` denetiminin neden geri çağrılarda tetiklenmediğini de
açıklar: parametre tipsiz görünse de bağlamdan gelen bir tipi vardır.

#### Dönüş Tipi Çıkarımı ve Hatanın Yeri

Fonksiyon dönüş tipleri de çıkarılır. Yukarıdaki bildirim dosyasında `ortalama`
fonksiyonunun dönüş tipi yazılmadığı hâlde `number` olarak görünüyor.

Çıkarımın rahatlığının bir bedeli vardır ve bu bedel, hatanın **nerede** bildirildiğinde
ortaya çıkar. Önce dönüş tipi yazılmamış hâli:

```typescript
function olcumOlustur(kimlik: string, ham: string) {
  return { kimlik, deger: ham };
}

const kayit = olcumOlustur("s-01", "21.4");
const yuvarlanmis: string = kayit.deger.toFixed(1);
console.log(yuvarlanmis);
```

```text
d2.ts(6,41): error TS2551: Property 'toFixed' does not exist on type 'string'. Did you mean 'fixed'?
```

Hata altıncı satırda, yani fonksiyonun **kullanıldığı** yerde bildirilir. Oysa yanlış olan
şey ikinci satırdadır: `deger` alanına dizgi konmuştur. Aynı fonksiyon, modelin tipiyle
birlikte yazıldığında:

```typescript
type Olcum = { kimlik: string; deger: number };

function olcumOlustur(kimlik: string, ham: string): Olcum {
  return { kimlik, deger: ham };
}

console.log(olcumOlustur("s-01", "21.4"));
```

```text
d3.ts(4,20): error TS2322: Type 'string' is not assignable to type 'number'.
```

Tanı artık dördüncü satırda — hatanın kaynağında. Fark, tek bir fonksiyonda önemsiz
görünür; onlarca modülden geçen bir çağrı zincirinde ise hatanın kaynağını bulma süresini
belirler.

Buradan pratik bir kural çıkar: **dışa açılan sınırlarda tip yazılır, iç ayrıntılarda
çıkarıma bırakılır.** Bir modülün dışa aktardığı fonksiyonların parametre ve dönüş
tipleri, bir sınıfın genel yöntemleri ve modülün veri tipleri elle yazılır. Yerel
değişkenler, geri çağrı parametreleri ve özel yardımcı fonksiyonlar çıkarıma bırakılır.

Bu kuralın ikinci gerekçesi, tip açıklamasının bir **sözleşme** olmasıdır. Yazılmış bir
dönüş tipi, gövde değiştiğinde sözleşmenin bozulup bozulmadığını sınar. Çıkarılan dönüş
tipi ise gövdeyle birlikte sessizce değişir — ve sözleşmenin değiştiği, ancak kullanan
kodda bir hata belirdiğinde fark edilir.

#### Çıkarımın Sınırları

Çıkarım her boşluğu dolduramaz. Üç tipik durum vardır.

**Boş kapla başlayan birikeç.** Yerel bir `const gecerliler = []` bildiriminde derleyici
eleman tipini sonraki eklemelerden toparlar. Bu, hatanın yine uzağa taşınması demektir:

```typescript
type Olcum = { kimlik: string; deger: number };

function suz(kayitlar: Olcum[]): Olcum[] {
  const gecerliler = [];
  for (const k of kayitlar) {
    if (k.deger > 0) {
      gecerliler.push(k.kimlik);
    }
  }
  return gecerliler;
}

console.log(suz([{ kimlik: "s-01", deger: 21.4 }]));
```

```text
bos2.ts(10,3): error TS2322: Type 'string[]' is not assignable to type 'Olcum[]'.
  Type 'string' is not assignable to type 'Olcum'.
```

Yanlış olan yedinci satırdır — kaydın kendisi yerine kimliği eklenmiştir — ama tanı
onuncu satırda çıkar. `const gecerliler: Olcum[] = []` yazılsaydı tanı eklemenin yapıldığı
satırda görünürdü.

**Dışarıdan gelen veri.** `JSON.parse` çağrısının dönüş tipi `any`dir; çıkarımın
kullanacağı bir bilgi yoktur. Önceki derste kurulan kural burada geçerlidir — dönüş
`unknown` olarak alınır ve sınanır.

**Aşırı geniş çıkarım.** Bir nesne sabitinden çıkarılan tip genellikle amaçlanandan
gevşektir; yukarıdaki `birim: string` örneği bunun tipik hâlidir. Amaçlanan tip
yazıldığında derleyici hem daraltır hem de sabitin o tipe uyduğunu sınar.

#### Özet

- Tip çıkarımı, bir ifadenin tipini ilk değerinden, bağlamından ve dönüş ifadelerinden
  hesaplar; çıkarılan tipler bildirim dosyası üretilerek görüntülenebilir.
- Değeri değişebilen bildirimlerde sabit tipler genişletilir; `const` bildirimi bağlamayı
  sabitlediği için sabit tip korunur, ancak nesne alanları yine genişletilir.
- Bağlamsal tipleme, bir ifadenin tipini beklendiği yerden alır; geri çağrı parametreleri
  bu yolla tiplenir.
- Dönüş tipi yazılmadığında hata, fonksiyonun kullanıldığı yerde bildirilir; yazıldığında
  kaynağında bildirilir.
- Dışa açılan sınırlarda tip yazılır, iç ayrıntılar çıkarıma bırakılır; tip açıklaması aynı
  zamanda gövde değiştiğinde sınanan bir sözleşmedir.

#### Sonraki Adım

Ölçüm kaydının `birim` alanı üç değerden birini alabiliyordu ve bu, birbirinin alternatifi
olan tiplerin birleştirilmesiyle yazılmıştı. Aynı işlemin bir de kesişim yönü vardır: iki
tipin gerekliliklerini aynı anda taşıyan değerler. Sonraki ders, birleşim ve kesişim
tiplerinin kurallarını, birleşim üzerinde hangi işlemlerin yapılabildiğini ve kesişimin ne
zaman boşa düştüğünü ele alacak.

### Birleşim ve Kesişim Tipleri

Source: https://academia.sh/tr/kurslar/typescript/birlesim-ve-kesisim-tipleri

Birleşim tipinde ortak üye kuralı, ayırt edici birleşim kalıbı, kesişimle tip birleştirme ve çakışan alanların boş tipe indirgenmesi.

Ölçüm kaydının `birim` alanı `"C" | "Pa" | "%"` biçiminde yazılmıştı: birbirinin
alternatifi olan üç tipin **birleşimi**. Aynı işlemin bir de kesişim yönü vardır — iki
tipin gerekliliklerini aynı anda taşıyan değerler.

Bu iki işleç, tip düzeyinde küme cebrinin karşılığıdır ve ölçüm kaydının bundan sonraki
gelişiminin çatısını kurar. Ders, birleşim üzerinde hangi işlemlerin yapılabildiğini,
ayırt edici alan kalıbını ve kesişimin ne zaman boşa düştüğünü ele alır.

#### Birleşim: Değerlerin Toplamı

`A | B` tipi, `A` tipindeki **veya** `B` tipindeki tüm değerleri kapsar. Değer kümeleri
düzeyinde bu bir birleşim işlemidir:

$$
\text{values}(A \mid B) = \text{values}(A) \cup \text{values}(B)
$$

Bir birleşim değeri elde eden kod, o değerin hangi üyeden geldiğini bilmez. Bu nedenle
üzerinde yalnızca **her iki üyede de bulunan** işlemleri yapabilir:

```typescript
type SayisalOlcum = { kimlik: string; deger: number };
type MetinselOlcum = { kimlik: string; metin: string };
type Kayit = SayisalOlcum | MetinselOlcum;

function yaz(kayit: Kayit): void {
  console.log(kayit.kimlik);
  console.log(kayit.deger);
}

yaz({ kimlik: "s-01", deger: 21.4 });
```

```text
e1.ts(7,21): error TS2339: Property 'deger' does not exist on type 'Kayit'.
  Property 'deger' does not exist on type 'MetinselOlcum'.
```

`kimlik` erişimi geçerlidir, çünkü her iki üyede de bulunur. `deger` erişimi reddedilir,
çünkü değer `MetinselOlcum` olabilir. Tanının ikinci satırı hangi üyenin engel olduğunu
söyler.

Buradan bir ters orantı çıkar ve ilk bakışta şaşırtıcıdır: **değer kümesi büyüdükçe
kullanılabilir üye kümesi küçülür.** Birleşime yeni bir üye eklemek, o tiple yapılabilecek
işlemleri azaltır. Küme dilinde: değerlerin birleşimi, güvenceli üyelerin kesişimidir.

#### Ayırt Edici Birleşim

Birleşim üyelerinden yalnızca birinde bulunan bir üyeye erişebilmek için, değerin hangi
üyeden geldiğinin **kanıtlanması** gerekir. En kullanışlı kanıt biçimi, her üyeye ortak
adlı ama farklı sabit tipli bir alan koymaktır:

```typescript
type Olcum =
  | { durum: "gecerli"; kimlik: string; deger: number }
  | { durum: "hatali"; kimlik: string; kod: string };

function ozet(olcum: Olcum): string {
  if (olcum.durum === "gecerli") {
    return `${olcum.kimlik}: ${olcum.deger}`;
  }
  return `${olcum.kimlik}: hata ${olcum.kod}`;
}

console.log(ozet({ durum: "gecerli", kimlik: "s-01", deger: 21.4 }));
console.log(ozet({ durum: "hatali", kimlik: "s-02", kod: "E17" }));
```

Bu dosya tip denetiminden geçer ve çıktısı şudur:

```text
s-01: 21.4
s-02: hata E17
```

`durum` alanına **ayırt edici** (discriminant) denir; bu kalıba da **ayırt edici birleşim**
(discriminated union). Çalışması için üç koşul gerekir: alanın adı tüm üyelerde aynı
olmalı, tipi sabit tip olmalı ve sabitler üyeler arasında farklı olmalıdır.

Koşullar sağlandığında derleyici `olcum.durum === "gecerli"` sınamasını bir kanıt olarak
kabul eder ve `if` gövdesinde tipi tek bir üyeye indirir. Bu indirgemeye **daraltma**
(narrowing) denir ve kuralları ayrı bir dersin konusudur. `if` bloğundan sonra kalan tek
üye `"hatali"` olduğu için `olcum.kod` erişimi de geçerlidir.

Ölçüm kaydının modeli bu noktada ilk yapısal kararını almış olur: geçerli ve hatalı kayıt
aynı tipin iki durumu değil, iki ayrı tiptir. Böylece `deger` alanı yalnızca gerçekten
bulunduğu durumda erişilebilir hâle gelir — "değer var mı" sorusu çalışma zamanından
derleme anına taşınmıştır.

#### Kesişim: Gerekliliklerin Toplamı

`A & B` tipi, hem `A` hem `B` olan değerleri kapsar. Nesne tiplerinde bu, alanların
birleştirilmesi anlamına gelir:

```typescript
type Olcum = { kimlik: string; deger: number };
type ZamanDamgali = { zaman: number };
type Kayit = Olcum & ZamanDamgali;

const kayit: Kayit = { kimlik: "s-01", deger: 21.4, zaman: 1706000000000 };
console.log(kayit.kimlik, kayit.deger, kayit.zaman);

const eksik: Kayit = { kimlik: "s-02", deger: 22.1 };
```

```text
e2.ts(8,7): error TS2322: Type '{ kimlik: string; deger: number; }' is not assignable to type 'Kayit'.
  Property 'zaman' is missing in type '{ kimlik: string; deger: number; }' but required in type 'ZamanDamgali'.
```

Birleşimdeki ters orantı burada düzelir: kesişimin değer kümesi daha küçük, kullanılabilir
üye kümesi daha büyüktür. Bir `Kayit` değeri hem `Olcum` hem `ZamanDamgali`nin tüm
alanlarını taşır.

Kesişimin tipik kullanımı, bağımsız olarak anlamlı olan nitelikleri ayrı tiplerde tutup
gerektiğinde birleştirmektir. Ölçüm kaydında zaman damgası, kaynak bilgisi ve doğrulama
sonucu ayrı ayrı tanımlanabilir; farklı bağlamlarda farklı bileşimleri kullanılır. Bu,
Veri Yapıları kursunda tanıtılan **soyut veri tipi** düşüncesinin tip düzeyindeki
karşılığıdır: bir tip, taşıdığı alanlardan çok karşıladığı sözleşmelerle tanımlanır.

#### Kesişim Boşa Düştüğünde

Kesişim her zaman anlamlı bir tip üretmez. İki ilkel tipin kesişiminde ortak değer
yoktur:

```typescript
type Imkansiz = string & number;

const bos: Imkansiz = "s-01";
console.log(bos);
```

```text
e6.ts(3,7): error TS2322: Type '"s-01"' is not assignable to type 'never'.
```

`string & number` tipi `never`e indirgenmiştir — hiçbir değer hem dizgi hem sayı olamaz.
Tanının doğrudan `never`den söz etmesi bu indirgemenin sonucudur.

Aynı durum nesne tiplerinde, çakışan bir alan üzerinden ortaya çıkar:

```typescript
type CelsiusKayit = { birim: "C"; deger: number };
type PascalKayit = { birim: "Pa"; deger: number };
type Ikisi = CelsiusKayit & PascalKayit;

declare const kayit: Ikisi;
const sinama: null = kayit;
const alan: null = kayit.birim;
```

```text
e4.ts(7,26): error TS2339: Property 'birim' does not exist on type 'never'.
  The intersection 'Ikisi' was reduced to 'never' because property 'birim' has conflicting types in some constituents.
```

Tanı, indirgemenin gerekçesini açıkça yazar: `birim` alanının tipleri bağdaşmadığı için
kesişim tümüyle `never` olmuştur. Altıncı satırın hata vermemesi de bunun sonucudur —
`never` her tipe atanabilir.

Bu davranış bir tasarım uyarısıdır. Çakışan alanlar taşıyan iki tip birleştirilmek
isteniyorsa, doğru araç kesişim değil birleşimdir: değerler ya birinin ya diğerinin
kuralına uyar, ikisinin birden değil.

#### İki İşlecin Karşılaştırması

| Ölçüt | Birleşim `A \| B` | Kesişim `A & B` |
|---|---|---|
| Değer kümesi | `A` ve `B` kümelerinin birleşimi | `A` ve `B` kümelerinin kesişimi |
| Erişilebilir üyeler | Yalnızca ortak üyeler | Tüm üyeler |
| Kullanmadan önce | Daraltma gerekir | Gerekmez |
| Çakışan alan | Ayırt edici olarak kullanılır | Boş tipe indirger |
| Tipik kullanım | Bir değerin alabildiği durumlar | Bağımsız niteliklerin bileşimi |

Tablo, iki işlecin birbirinin yerine geçmediğini gösterir. Birleşim bir **seçim**
modellemek içindir: kayıt ya geçerlidir ya hatalıdır. Kesişim bir **toplam** modellemek
içindir: kayıt hem kimlikli hem zaman damgalıdır.

#### Özet

- Birleşim tipinin değer kümesi üyelerin birleşimidir; erişilebilir üye kümesi ise
  üyelerin kesişimidir.
- Ayırt edici birleşim, her üyeye aynı adlı ve farklı sabit tipli bir alan koyar; bu alan
  üzerinde yapılan sınama daraltmayı mümkün kılar.
- Kesişim tipi, nesne tiplerinin alanlarını birleştirir ve tüm gereklilikleri aynı anda
  dayatır.
- İki ilkel tipin kesişimi ile çakışan alan taşıyan nesne tiplerinin kesişimi `never`e
  indirgenir; derleyici indirgeme gerekçesini tanıda belirtir.
- Birleşim bir seçimi, kesişim bir toplamı modeller; çakışan alanlar için doğru araç
  birleşimdir.

#### Sonraki Adım

Ayırt edici birleşimin çalışması için alanların sabit tipte olması gerekiyordu. Oysa tip
çıkarımı dersinde görüldüğü gibi, bir nesne sabitinin alanları kendiliğinden genişletilir
ve sabit tip kaybolur. Sonraki ders bu genişletmeyi durduran araçları — sabit tipleri,
`as const` yazımını ve tipin sınanmasını sağlayan `satisfies` işlecini — ele alacak.

### Sabit Tipler ve as const

Source: https://academia.sh/tr/kurslar/typescript/sabit-tipler-ve-as-const

Tek değerli sabit tipler, as const ile genişletmenin durdurulması, sabit bir listeden birleşim tipi türetme ve satisfies işlecinin tip açıklamasından farkı.

Ayırt edici birleşimin çalışması için alanların sabit tipte olması gerekiyordu. Tip
çıkarımı dersi ise ters yönde bir kural koymuştu: nesne alanlarının tipi kendiliğinden
genişletilir ve sabit tip kaybolur.

Bu ders bu gerilimi çözer. Önce sabit tiplerin ne olduğu netleştirilir, sonra genişletmeyi
durduran `as const` yazımı, en sonunda da tipi daraltmadan sınayan `satisfies` işleci ele
alınır.

Terim uyarısı: burada **sabit tip** (literal type) ile kastedilen, tek bir değeri içeren
bir tiptir. Programlama Temelleri kursundaki **sabit** (constant) kavramıyla — program
boyunca aynı değere bağlı ad — akrabadır ama aynı şey değildir; biri değerin tipiyle,
diğeri bağlamanın değişmezliğiyle ilgilidir.

#### Sabit Tip Nedir

Her değerin, yalnızca kendisini içeren bir tipi vardır. `"C"` bir dizgi değeridir, `"C"`
aynı zamanda tek elemanlı bir tiptir. Bu tiplere sabit tip denir ve dizgi, sayı ve
mantıksal değerler için tanımlıdır.

Sabit tipler tek başlarına pek kullanışlı değildir — `const x: "C" = "C"` bildiriminin
söylediği ek bir şey yoktur. Değerleri, birleşimde ortaya çıkar: `"C" | "Pa" | "%"` tipi,
üç değerli bir kümedir ve dördüncü bir değeri reddeder.

#### Genişletme ve `as const`

Aynı nesne sabitinin iki biçimde bildirilmesi, çıkarılan tipleri karşılaştırmayı sağlar:

```typescript
export const dogrudan = { kimlik: "s-01", birim: "C", deger: 21.4 };
export const dondurulmus = { kimlik: "s-01", birim: "C", deger: 21.4 } as const;
export const birimler = ["C", "Pa", "%"];
export const birimlerSabit = ["C", "Pa", "%"] as const;
```

Dosya `sabit.ts` adıyla kaydedilip
`tsc --strict --target es2022 --declaration --emitDeclarationOnly sabit.ts` komutuyla
derlendiğinde üretilen `sabit.d.ts` dosyası:

```typescript
export declare const dogrudan: {
    kimlik: string;
    birim: string;
    deger: number;
};
export declare const dondurulmus: {
    readonly kimlik: "s-01";
    readonly birim: "C";
    readonly deger: 21.4;
};
export declare const birimler: string[];
export declare const birimlerSabit: readonly ["C", "Pa", "%"];
```

`as const` yazımı üç şeyi birden yapar:

1. Alanların tiplerini **genişletmez**; her alan kendi sabit tipini korur.
2. Tüm alanları **salt okunur** (readonly) işaretler.
3. Dizi sabitini dizi değil **demet** olarak tipler: `string[]` yerine
   `readonly ["C", "Pa", "%"]`.

Üçüncü madde önemlidir. `birimler` dizisinin tipi eleman sayısını unutmuştur;
`birimlerSabit` demetinin tipi hem sayıyı hem sırayı hem de her konumun değerini bilir.

Salt okunur işaretlemesi de tip düzeyinde bir güvence sağlar:

```typescript
const birimler = ["C", "Pa", "%"] as const;
birimler.push("F");
```

```text
f2.ts(2,10): error TS2339: Property 'push' does not exist on type 'readonly ["C", "Pa", "%"]'.
```

#### `as const` Çalışma Zamanında Ne Bırakır

Kursun eksenini yeniden sınamak gerekir. Aşağıdaki dosya
`tsc --strict --target es2022 ac.ts` ile derlendiğinde:

```typescript
const birimler = ["C", "Pa", "%"] as const;
const olcum = { kimlik: "s-01", birim: "C" } as const;
console.log(birimler, olcum);
```

Üretilen JavaScript:

```javascript
"use strict";
const birimler = ["C", "Pa", "%"];
const olcum = { kimlik: "s-01", birim: "C" };
console.log(birimler, olcum);
```

`node ac.js` çıktısı `[ 'C', 'Pa', '%' ] { kimlik: 's-01', birim: 'C' }` olur.

`as const` **tamamen silinmiştir**. Dizi çalışma zamanında sıradan bir dizidir; `push`
çağrısı JavaScript tarafından kabul edilir. Salt okunurluk, derleyicinin gördüğü kodda
geçerli bir sözdür — çalışma zamanı koruması değil. Gerçek değiştirilemezlik için
`Object.freeze` gerekir ve o da yüzeyseldir.

Aynı sınır, `as const` ile işaretlenmiş bir değerin salt okunur olmayan bir tipe
atanmasında da görülür:

```typescript
type Birim = "C" | "Pa" | "%";
type Olcum = { kimlik: string; deger: number; birim: Birim };

const ham = { kimlik: "s-01", deger: 21.4, birim: "C" } as const;
const kayit: Olcum = ham;
console.log(kayit.birim);

kayit.deger = 22.1;
ham.deger = 22.1;
```

```text
f6.ts(9,5): error TS2540: Cannot assign to 'deger' because it is a read-only property.
```

Sekizinci satır hata vermez. `kayit` bildiriminin tipi `Olcum`dur ve `Olcum` salt okunur
değildir; aynı nesneye salt okunur olmayan bir başvuru elde edilmiştir. Salt okunurluk
nesnenin değil, **başvurunun** özelliğidir.

Beşinci satırın kabul edilmesi ise `as const` yazımının çözdüğü asıl sorunu gösterir:
`as const` olmadan `birim` alanı `string`e genişler ve atama reddedilirdi.

#### Sabit Listeden Tip Türetme

`as const` ile bildirilmiş bir demetten, elemanlarının birleşimi olan bir tip
türetilebilir:

```typescript
const birimler = ["C", "Pa", "%"] as const;
type Birim = (typeof birimler)[number];

const gecerli: Birim = "Pa";
console.log(gecerli, birimler.length);

const gecersiz: Birim = "F";
```

```text
f1.ts(7,7): error TS2322: Type '"F"' is not assignable to type '"%" | "C" | "Pa"'.
```

İki adım vardır. `typeof birimler` yazımı, bir **değerin** tipini alır — burada
`readonly ["C", "Pa", "%"]`. Ardından `[number]` indeksi, demetin sayısal indeksle
erişilebilen tüm elemanlarının tiplerinin birleşimini verir. Tanı, üç sabit değerin
birleşimini yazar; derleyici üyeleri kendi iç sırasıyla (`"%" | "C" | "Pa"`) dizer, dizideki
sıra korunmaz. Birleşimin kendisi bu türetmenin sonucudur.

Bu kalıbın değeri, aynı bilginin iki kez yazılmasını önlemesidir. Değerler listesi tek
yerde durur; hem çalışma zamanında (döngüyle gezilebilir, doğrulamada kullanılabilir) hem
de tip düzeyinde kullanılabilir. Numaralandırmanın verdiği "tüm değerleri sayabilme"
yeteneği, çalışma zamanı kodu üretmeden elde edilmiş olur.

#### `satisfies`: Sınamak Ama Daraltmamak

Bir nesne sabitine tip yazmak iki şeyi birden yapar: sabiti sınar **ve** bildirimin tipini
o tipe sabitler. İkincisi her zaman istenmez:

```typescript
type Aralik = { alt: number; ust: number };

const aciklamali: Record<string, Aralik> = {
  sicaklik: { alt: -40, ust: 85 },
};

const sinanan = {
  sicaklik: { alt: -40, ust: 85 },
} satisfies Record<string, Aralik>;

console.log(aciklamali.nem.ust);
console.log(sinanan.nem.ust);
```

```text
f4.ts(12,21): error TS2339: Property 'nem' does not exist on type '{ sicaklik: { alt: number; ust: number; }; }'.
```

On birinci satır hata vermez. `aciklamali` bildiriminin tipi `Record<string, Aralik>`tir;
bu tip "her dizgi anahtar için bir aralık" der, dolayısıyla `nem` anahtarı da geçerli
sayılır. Çalışma zamanında böyle bir anahtar yoktur ve program çöker.

`satisfies` işleci sabitin verilen tipe uyduğunu **sınar**, ama bildirimin tipini
değiştirmez. `sinanan` bildiriminin tipi, sabitten çıkarılan dar tiptir; olmayan anahtara
erişim on ikinci satırda yakalanır.

Sınama yönü de çalışır:

```typescript
type Aralik = { alt: number; ust: number };

const araliklar = {
  sicaklik: { alt: -40, ust: "85" },
} satisfies Record<string, Aralik>;

console.log(araliklar.sicaklik.ust);
```

```text
f5.ts(4,25): error TS2322: Type 'string' is not assignable to type 'number'.
```

Üç yazımın karşılaştırması:

| Yazım | Sabiti sınar | Bildirimin tipi |
|---|---|---|
| `const x: T = {...}` | Evet | `T` |
| `const x = {...} as T` | Hayır (zorlar) | `T` |
| `const x = {...} satisfies T` | Evet | Sabitten çıkarılan dar tip |

Orta satır ilk derste tanıtılan tip iddiasıdır ve bir borçtur. Alt satır, hem sınamayı hem
de çıkarımın verdiği ayrıntıyı korur.

#### Özet

- Sabit tip, tek bir değeri içeren tiptir; kullanışlılığı birleşim içinde ortaya çıkar ve
  bağlamanın değişmezliğini anlatan "sabit" kavramından ayrıdır.
- `as const` yazımı genişletmeyi durdurur, alanları salt okunur işaretler ve dizi
  sabitlerini demet olarak tipler.
- `as const` çalışma zamanında tamamen silinir; salt okunurluk nesnenin değil başvurunun
  özelliğidir ve gerçek değiştirilemezlik sağlamaz.
- Sabit bir demetten `(typeof x)[number]` yazımıyla elemanlarının birleşimi türetilir; aynı
  bilgi hem değer hem tip düzeyinde tek yerde tutulur.
- `satisfies` işleci sabiti verilen tipe karşı sınar ancak bildirimin dar tipini korur; tip
  açıklaması sınar ve genişletir, tip iddiası ise yalnızca zorlar.

#### Sonraki Adım

Bu konu, tek tek değerlerin tiplerini kurdu. Sırada bu tiplerin adlandırılması ve
paylaşılması var: bir modülün dışa açtığı sözleşme nasıl yazılır, aynı sözleşmeyi
karşılayan farklı gerçekleştirimler nasıl tanımlanır? Sonraki konu arayüzlerle başlayacak
ve ölçüm kaydının modelini yeniden kullanılabilir bir sözleşmeye dönüştürecek.

## Yapılar ve Sözleşmeler

Arayüzler, tip takma adları, sınıflar ve erişim belirteçleri, fonksiyon tipleri ve yapısal tip uyumluluğunun kuralları.

### Arayüzler

Source: https://academia.sh/tr/kurslar/typescript/arayuzler

Arayüzle yapısal sözleşme tanımı, seçmeli ve salt okunur alanlar, genişletme, indeks imzaları ve bildirim birleştirme.

Önceki konu, tek tek değerlerin tiplerini kurdu. Bu konu, o tipleri adlandırmaya ve
paylaşmaya geçer: bir modülün dışa açtığı sözleşme nasıl yazılır, aynı sözleşmeyi
karşılayan farklı gerçekleştirimler nasıl tanımlanır?

**Arayüz** (interface), bir değerin taşıması gereken alanları ve yöntemleri adlandıran
bildirimdir. Veri Yapıları kursunda tanıtılan **soyut veri tipi** düşüncesinin doğrudan
karşılığıdır: yığıtın ne olduğu, iç gösterimiyle değil `it`, `çek`, `tepe` ve `boş_mu`
işlemleriyle tanımlanır. Arayüz de bir tipi, iç yapısıyla değil sunduğu işlemlerle
tanımlar.

#### Arayüz Bildirimi

Ölçüm kaydının modeli arayüz biçiminde yazılabilir:

```typescript
type Birim = "C" | "Pa" | "%";

interface Olcum {
  readonly kimlik: string;
  sensor: string;
  deger: number;
  birim: Birim;
  zaman: number;
  not?: string;
}

const kayit: Olcum = {
  kimlik: "s-01",
  sensor: "sicaklik",
  deger: 21.4,
  birim: "C",
  zaman: 1706000000000,
};

console.log(kayit.not);
kayit.deger = 22.1;
kayit.kimlik = "s-02";
```

```text
g1.ts(22,7): error TS2540: Cannot assign to 'kimlik' because it is a read-only property.
```

Bildirimde iki yeni işaret var.

**`readonly kimlik`** alanı, atamayı derleme anında engeller. Yirmi birinci satırdaki
`deger` ataması geçerlidir, yirmi ikincisi değildir. Önceki derste kurulan sınır burada da
geçerli: `readonly` çalışma zamanında silinir ve nesnenin kendisini korumaz.

**`not?: string`** alanı **seçmeli** (optional) bildirilmiştir. Nesne sabitinde
bulunmamasına rağmen atama kabul edilir; `kayit.not` erişiminin tipi `string | undefined`
olur ve doğrudan kullanılamadan önce daraltılması gerekir. Yirminci satır yalnızca
yazdırdığı için sorun çıkmaz, çıktısı `undefined`dır.

Seçmeli alan, "bu bilgi bulunmayabilir" durumunu tip düzeyine taşır. Alternatifi — alanı
zorunlu yapıp boş dizgi koymak — bilgiyi kaybeder: boş dizginin "ölçülmedi" mi yoksa "not
boştu" mu anlamına geldiği belirsizdir.

#### Genişletme

Bir arayüz, başka bir arayüzün tüm gerekliliklerini devralabilir:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

interface KonumluOlcum extends Olcum {
  enlem: number;
  boylam: number;
}

const kayit: KonumluOlcum = {
  kimlik: "s-01",
  deger: 21.4,
  enlem: 41.0,
  boylam: 29.0,
};
console.log(kayit.kimlik, kayit.enlem);

const eksik: KonumluOlcum = { kimlik: "s-02", deger: 22.1 };
```

```text
g2.ts(19,7): error TS2739: Type '{ kimlik: string; deger: number; }' is missing the following properties from type 'KonumluOlcum': enlem, boylam
```

`extends` yazımı, kesişim tipiyle aynı sonucu üretir: `KonumluOlcum` tipi hem kendi
alanlarını hem `Olcum`un alanlarını gerektirir. İki fark vardır.

Birincisi, `extends` uyumluluğu **bildirim anında** sınar:

```typescript
interface Temel {
  birim: string;
}

interface Turemis extends Temel {
  birim: number;
}

declare const x: Turemis;
console.log(x.birim);
```

```text
g6.ts(5,11): error TS2430: Interface 'Turemis' incorrectly extends interface 'Temel'.
  Types of property 'birim' are incompatible.
    Type 'number' is not assignable to type 'string'.
```

Tanı, çakışmanın bulunduğu bildirimi gösterir. Aynı çakışma kesişim tipiyle yazılsaydı,
önceki derste görüldüğü gibi tip sessizce `never`e indirgenir ve hata ancak kullanım
yerinde ortaya çıkardı.

İkincisi, `extends` zinciri tanı iletilerinde ad olarak görünür ve okunabilirliği artırır.

Bu kalıtım, Programlama Temelleri kursundaki sınıf kalıtımından ayrıdır: burada devralınan
şey davranış değil, yalnızca sözleşmedir. Arayüz gerçekleştirim içermez.

#### Bildirim Birleştirme

Arayüzlerin, tip takma adlarında bulunmayan bir özelliği vardır: aynı adla birden çok kez
bildirilebilirler. Bildirimler birleştirilir.

```typescript
interface Olcum {
  kimlik: string;
}

interface Olcum {
  deger: number;
}

const kayit: Olcum = { kimlik: "s-01", deger: 21.4 };
console.log(kayit.kimlik, kayit.deger);

const eksik: Olcum = { kimlik: "s-02" };
```

```text
g3.ts(12,7): error TS2741: Property 'deger' is missing in type '{ kimlik: string; }' but required in type 'Olcum'.
```

İki bildirim tek bir arayüz oluşturmuştur; on ikinci satırın reddedilmesi bunun kanıtıdır.

Bu davranış bir kolaylık gibi görünse de asıl amacı farklıdır: **var olan bir tipi
dışarıdan genişletmeyi** sağlar. Bir kütüphanenin tanımladığı arayüze, o kütüphaneyi
değiştirmeden alan eklenebilir. Bildirim Dosyaları dersinde bu yeteneğin asıl kullanım
yeri görülecektir.

Aynı yetenek bir risk de taşır: bir arayüzün tanımı tek bir dosyada tam olarak
görülmeyebilir. Kendi kodunda tip birleştirmeye dayanmak, tanımın izini sürmeyi
zorlaştırır.

#### İndeks İmzaları

Alan adlarının önceden bilinmediği durumlar için **indeks imzası** (index signature)
yazılır:

```typescript
interface EtiketliOlcum {
  kimlik: string;
  deger: number;
  [etiket: string]: string | number;
}

const kayit: EtiketliOlcum = {
  kimlik: "s-01",
  deger: 21.4,
  konum: "kazan-2",
  vardiya: 3,
};

console.log(kayit.konum, kayit["vardiya"]);
console.log(kayit.olmayanEtiket);
```

Bu dosya tip denetiminden geçer. Çıktısı:

```text
kazan-2 3
undefined
```

İki kural işliyor. Birincisi, indeks imzası varken adı belirtilmiş alanların tipleri
imzanın tipine **uymak zorundadır**; `kimlik: string` ve `deger: number` alanları
`string | number` birleşiminin içinde kaldıkları için kabul edilir. Bir `boolean` alanı
eklenseydi tanı gelirdi.

İkincisi ve daha önemlisi: **indeks imzası, olmayan bir anahtara erişimi hata saymaz.**
Son satırdaki `kayit.olmayanEtiket` ifadesi derleyiciye göre `string | number` tipindedir,
çalışma zamanında ise `undefined`dır. Tip sistemi burada gerçekliğin ötesinde bir söz verir.

Bu boşluğu kapatan bir derleyici seçeneği vardır — indeksli erişimlerin sonucuna
`undefined` ekleyen bir katılık bayrağı — ve Derleyici Yapılandırması dersinde ele
alınacaktır. Genel kural şudur: anahtar kümesi biliniyorsa indeks imzası yerine alanları
tek tek yazmak ya da eşlenmiş tip kullanmak daha güvenlidir.

#### Sözleşme Olarak Arayüz

Arayüzün asıl gücü, veri biçimini tanımlamaktan çok **davranış sözleşmesi** kurmakta
ortaya çıkar. Ölçüm kaydı modelinin bir sonraki adımı, kayıtların nereden geldiğini
soyutlamaktır:

```typescript
type Olcum = { kimlik: string; deger: number };

interface OlcumKaynagi {
  readonly ad: string;
  sonraki(): Olcum | null;
}

function diziKaynagi(ad: string, kayitlar: readonly Olcum[]): OlcumKaynagi {
  let sira = 0;
  return {
    ad,
    sonraki() {
      return sira < kayitlar.length ? kayitlar[sira++] : null;
    },
  };
}

function sabitKaynak(ad: string, deger: number): OlcumKaynagi {
  return {
    ad,
    sonraki() {
      return { kimlik: ad, deger };
    },
  };
}

function ilkIkisi(kaynak: OlcumKaynagi): string {
  const a = kaynak.sonraki();
  const b = kaynak.sonraki();
  return `${kaynak.ad}: ${a?.deger ?? "-"} ${b?.deger ?? "-"}`;
}

console.log(ilkIkisi(diziKaynagi("dosya", [{ kimlik: "s-01", deger: 21.4 }])));
console.log(ilkIkisi(sabitKaynak("sabit", 20)));
```

Çıktı:

```text
dosya: 21.4 -
sabit: 20 20
```

`ilkIkisi` fonksiyonu iki kaynağın hiçbirini tanımaz; yalnızca `OlcumKaynagi`
sözleşmesini bilir. Bu, Programlama Temelleri kursunda tanıtılan **arayüz–gerçekleştirim**
ayrımının tip düzeyindeki hâlidir: sözleşme ile ona uyan kod ayrı ayrı değişebilir.

Dikkat edilmesi gereken bir nokta var: hiçbir gerçekleştirim `OlcumKaynagi` adını
yazmıyor. `diziKaynagi` fonksiyonunun döndürdüğü nesne sabitinde "bu bir ölçüm kaynağıdır"
diye bir bildirim yok; nesne yalnızca gerekli alanlara sahip olduğu için kabul ediliyor.
Bu, TypeScript'in tip uyumluluğunun temelidir ve Yapısal Tip Uyumluluğu dersinde
ayrıntılandırılacaktır.

Dönüş tipinin `Olcum | null` olması da bir tasarım kararıdır. `ilkIkisi` içinde
`a?.deger` ve `?? "-"` yazımları bu birleşimi ele almak zorundadır; derleyici, `null`
olasılığının atlanmasına izin vermez.

#### Özet

- Arayüz, bir değerin taşıması gereken alanları ve yöntemleri adlandırır; soyut veri
  tipi düşüncesinin tip düzeyindeki karşılığıdır.
- `readonly` alanlar derleme anında atamayı engeller, seçmeli alanlar `undefined`
  olasılığını tipe taşır.
- `extends` yazımı sözleşmeleri birleştirir ve çakışmayı bildirim anında sınar; gerçekleştirim
  devretmez.
- Aynı adlı arayüz bildirimleri birleştirilir; bu yetenek dış tipleri genişletmek içindir ve
  kendi kodunda tanımın izini sürmeyi zorlaştırır.
- İndeks imzası bilinmeyen anahtarları tipler ama olmayan anahtara erişimi hata saymaz;
  anahtar kümesi biliniyorsa alanlar tek tek yazılır.

#### Sonraki Adım

Aynı ölçüm modeli, `interface` yerine `type` anahtar sözcüğüyle de yazılabilirdi — nitekim
önceki konuda öyle yazılmıştı. İki bildirim biçimi çoğu durumda birbirinin yerine geçer,
ama tam olarak eşdeğer değildir. Sonraki ders farkları sıralayacak ve hangi durumda
hangisinin seçileceğine dair bir ölçüt kuracak.

### Tip Takma Adları

Source: https://academia.sh/tr/kurslar/typescript/tip-takma-adlari

type bildiriminin arayüzden farkları: her tipi adlandırabilme, bildirim birleştirmenin bulunmaması, döngüsel başvuru kuralları, takma adla sınıf sözleşmesi ve seçim ölçütü.

Aynı ölçüm modeli önceki konuda `type` ile, bu konunun ilk dersinde `interface` ile
yazıldı. İki bildirim biçimi çoğu durumda birbirinin yerine geçer; eşdeğer değildir.

**Tip takma adı** (type alias), var olan bir tipe ad veren bildirimdir. Yeni bir tip
yaratmaz — `type Kimlik = string` bildiriminden sonra `Kimlik` ile `string` aynı tiptir ve
birbirinin yerine kullanılabilir. Arayüz ise adlandırılmış bir nesne tipi **tanımlar**.
Bu ders, farkın nerede ortaya çıktığını ve hangisinin ne zaman seçileceğini kurar.

#### Her Tipi Adlandırabilme

Takma ad, herhangi bir tipe ad verebilir. Arayüz yalnızca nesne ve çağrılabilir yapıları
tanımlayabilir:

```typescript
type Birim = "C" | "Pa" | "%";
type Kimlik = string;
type Aralik = [alt: number, ust: number];
type Donusturucu = (deger: number) => number;

const birim: Birim = "C";
const aralik: Aralik = [-40, 85];
const kelvine: Donusturucu = (d) => d + 273.15;

console.log(birim, aralik, kelvine(21.4));
```

Çıktı:

```text
C [ -40, 85 ] 294.54999999999995
```

Dördünün hiçbiri arayüzle yazılamaz. Birleşim, ilkel tip ve demet nesne tipi değildir;
fonksiyon tipi arayüzle yazılabilir ama çok daha uzun bir yazım gerektirir. Sonraki
konudaki eşlenmiş, koşullu ve şablon dizgi tipleri de yalnızca takma adla adlandırılabilir.

Çıktının son değerine dikkat: `21.4 + 273.15` işlemi `294.55` değil
`294.54999999999995` verir. Tip sistemi `number` tipini doğru saymıştır; kayan noktalı
gösterimin kesinlik sınırı ise onun alanı değildir. Bu, ilk derste sayılan
"kanıtlanmayanlar" listesinin somut bir örneğidir.

#### Bildirim Birleştirme Yok

Arayüzler aynı adla yeniden bildirilip birleştirilebiliyordu. Takma adlarda bu yoktur:

```typescript
type Olcum = { kimlik: string };
type Olcum = { deger: number };
```

```text
h2.ts(1,6): error TS2300: Duplicate identifier 'Olcum'.
h2.ts(2,6): error TS2300: Duplicate identifier 'Olcum'.
```

Tanı iki kez gelir, çünkü her iki bildirim de çakışmanın tarafıdır.

Bu, takma adın sınırı değil güvencesidir: bir takma adın tanımı, yazıldığı yerde tamdır.
Başka bir dosyanın o tipe alan eklemesi olanaksızdır. Kendi kod tabanının veri
modellerinde bu öngörülebilirlik genellikle istenir.

Karşı yönde, bir arayüz **genişletme** yoluyla takma addan türetilebilir:

```typescript
type SayisalOlcum = { kimlik: string; deger: number };

interface ZamanliOlcum extends SayisalOlcum {
  zaman: number;
}

const k: ZamanliOlcum = { kimlik: "s-01", deger: 21.4, zaman: 1 };
console.log(k);
```

Bu dosya hatasız derlenir. Ancak takma ad bir nesne tipi değilse genişletme başarısız
olur:

```typescript
type SayisalOlcum = { kimlik: string; deger: number };
type MetinselOlcum = { kimlik: string; metin: string };
type Kayit = SayisalOlcum | MetinselOlcum;

interface Genisletme extends Kayit {
  zaman: number;
}
```

```text
h7.ts(5,30): error TS2312: An interface can only extend an object type or intersection of object types with statically known members.
```

Bir birleşimi genişletmenin anlamı yoktur: `Genisletme` tipinin hangi üyeyi devralacağı
belirsizdir. Aynı işlemi kesişimle yazmak ise geçerlidir —
`type Genisletme = Kayit & { zaman: number }` bildirimi, her iki üyeye zaman alanı ekleyen
bir birleşim üretir.

#### Döngüsel Başvuru

Ölçüm kayıtları iç içe geçen bir yapıya dönüştüğünde, tip kendisine başvurmak zorunda
kalır. Nesne alanı içindeki başvuru geçerlidir:

```typescript
type OlcumDugumu = {
  kimlik: string;
  deger: number;
  altKayitlar: OlcumDugumu[];
};

const kok: OlcumDugumu = {
  kimlik: "kazan-2",
  deger: 21.4,
  altKayitlar: [{ kimlik: "s-01", deger: 21.4, altKayitlar: [] }],
};

function toplamDugum(dugum: OlcumDugumu): number {
  return 1 + dugum.altKayitlar.reduce((t, a) => t + toplamDugum(a), 0);
}

console.log(toplamDugum(kok));
```

Dosya hatasız derlenir, çıktısı `2`dir.

Buna karşılık **doğrudan** döngüsel bir başvuru reddedilir:

```typescript
type Zincir = Zincir | null;
declare const z: Zincir;
console.log(z);
```

```text
h3.ts(1,6): error TS2456: Type alias 'Zincir' circularly references itself.
```

Ayrım şudur: `OlcumDugumu` başvurusu bir nesne alanının içindedir ve derleyici o alanın
tipini yalnızca gerektiğinde çözer. `Zincir` bildirimi ise kendi tanımını çözmek için yine
kendini gerektirir; sonlanmayan bir açılımdır. Özyinelemeli tip tanımlarının kuralları
İleri Tipler konusunda ayrıntılı ele alınacaktır.

#### Takma Adla Sınıf Sözleşmesi

Bir sınıfın karşılayacağı sözleşme takma adla da yazılabilir. Takma ad bir nesne tipini
adlandırdığı sürece `implements` bildirimi arayüzdekiyle aynı biçimde çalışır:

```typescript
type Olcum = { kimlik: string; deger: number };

class DosyaKaydi implements Olcum {
  constructor(
    readonly kimlik: string,
    readonly deger: number,
  ) {}
}

const kayit: Olcum = new DosyaKaydi("s-01", 21.4);
console.log(kayit.kimlik, kayit.deger);
```

Çıktı `s-01 21.4`tür.

Sınır, genişletmedeki sınırla aynıdır: takma ad bir birleşimi adlandırıyorsa
gerçekleştirilemez.

```typescript
type SayisalOlcum = { kimlik: string; deger: number };
type MetinselOlcum = { kimlik: string; metin: string };
type Kayit = SayisalOlcum | MetinselOlcum;

class DosyaKaydi implements Kayit {
  constructor(readonly kimlik: string) {}
}
```

```text
u2.ts(5,29): error TS2422: A class can only implement an object type or intersection of object types with statically known members.
```

Gerekçe de aynıdır: sınıfın hangi üyeyi karşılayacağı belirsizdir. Bir sınıfın "ya şu ya bu
biçimde" olması, tek bir bildirimle ifade edilemez. Böyle bir modelde sözleşme, birleşimin
her üyesi için ayrı sınıflarla karşılanır ve birleşim yalnızca değer düzeyinde kullanılır.

#### Seçim Ölçütü

İki bildirim biçimi arasındaki farklar bir tabloda toplanabilir:

| Ölçüt | `interface` | `type` |
|---|---|---|
| Nesne tipi tanımlar | Evet | Evet |
| Birleşim, demet, ilkel adlandırır | Hayır | Evet |
| Aynı adla yeniden bildirim | Birleşir | `TS2300` |
| Genişletme | `extends` (bildirimde sınanır) | `&` (sessizce indirgenir) |
| Sınıfla gerçekleştirme | `implements` | `implements` (nesne tipiyse) |
| Eşlenmiş, koşullu tip | Hayır | Evet |

Tablodan uygulanabilir bir ölçüt çıkar:

**Nesne biçimindeki sözleşmeler için arayüz.** Bir veri kaydının alanları, bir servisin
sunduğu yöntemler, bir sınıfın karşılayacağı sözleşme. Genişletme sınandığı için hatalar
bildirim anında yakalanır; tanı iletileri ad taşır.

**Diğer her şey için takma ad.** Birleşimler, ayırt edici birleşimler, demetler, fonksiyon
tipleri, tip düzeyinde hesaplanan tipler. Bu tiplerin arayüz karşılığı ya yoktur ya
zorlamadır.

Bu kursta ölçüm kaydının kendisi arayüzle, durumları ve türetilmiş tipleri takma adla
yazılacaktır:

```typescript
interface Olcum {
  readonly kimlik: string;
  sensor: string;
  deger: number;
  birim: "C" | "Pa" | "%";
  zaman: number;
}

type OlcumDurumu =
  | { durum: "gecerli"; olcum: Olcum }
  | { durum: "hatali"; kimlik: string; kod: string };

function ozet(kayit: OlcumDurumu): string {
  return kayit.durum === "gecerli"
    ? `${kayit.olcum.kimlik}=${kayit.olcum.deger}${kayit.olcum.birim}`
    : `${kayit.kimlik}: ${kayit.kod}`;
}

console.log(
  ozet({
    durum: "gecerli",
    olcum: {
      kimlik: "s-01",
      sensor: "sicaklik",
      deger: 21.4,
      birim: "C",
      zaman: 1706000000000,
    },
  }),
);
console.log(ozet({ durum: "hatali", kimlik: "s-02", kod: "E17" }));
```

Çıktı:

```text
s-01=21.4C
s-02: E17
```

İki bildirim biçiminin iş bölümü burada görülür: `Olcum` bir kaydın biçimidir ve
büyüyebilir; `OlcumDurumu` ise kapalı bir seçenek kümesidir ve büyümesi kasıtlı bir karar
olmalıdır.

#### Özet

- Tip takma adı var olan bir tipe ad verir; birleşim, demet, ilkel ve fonksiyon tipleri
  dahil her tipi adlandırabilir.
- Arayüzler aynı adla yeniden bildirilip birleşir; takma adın ikinci bildirimi `TS2300`
  tanısı verir ve bu, tanımın tek yerde tam olmasını güvenceye alır.
- Bir arayüz nesne tipindeki takma adı genişletebilir; birleşim tipini genişletemez.
- Nesne alanı içindeki döngüsel başvuru geçerlidir; doğrudan döngüsel takma ad `TS2456`
  tanısı verir.
- Nesne tipini adlandıran bir takma ad `implements` ile gerçekleştirilebilir; birleşimi
  adlandıran takma ad `TS2422` tanısı verir.
- Ölçüt: nesne biçimindeki sözleşmeler arayüzle, birleşimler ve tip düzeyinde hesaplanan
  tipler takma adla yazılır.

#### Sonraki Adım

Buraya kadarki sözleşmeler yalnızca veriyi ve işlem imzalarını tanımladı; gerçekleştirim
her seferinde ayrı bir fonksiyondan geldi. Sınıflar bu ikisini bir arada tutar ve
TypeScript, sınıflara görünürlük denetimi ekler. Sonraki ders erişim belirteçlerini, soyut
sınıfları ve bunların derlenmiş çıktıda ne bıraktığını ele alacak.

### Sınıflar ve Erişim Belirteçleri

Source: https://academia.sh/tr/kurslar/typescript/siniflar-ve-erisim-belirtecleri

Sınıf alanlarının tiplenmesi, görünürlük belirteçlerinin derleme anındaki ve çalışma zamanındaki karşılıkları, parametre özellikleri, soyut sınıflar ve implements bildirimi.

Buraya kadarki sözleşmeler yalnızca veriyi ve işlem imzalarını tanımladı; gerçekleştirim
her seferinde ayrı bir fonksiyondan geldi. Sınıflar bu ikisini bir arada tutar.

JavaScript'te Nesneler ve Fonksiyonlar kursunda sınıf sözdiziminin prototip modeline
karşılığı kurulmuştu. TypeScript bu sözdizimine üç şey ekler: alanların ve yöntemlerin
tipleri, **görünürlük belirteçleri** (access modifier) ve soyut bildirimler. Bu eklerin
bazıları silinir, bazıları kod üretir; ayrım bu dersin merkezidir.

#### Alanların Tiplenmesi ve Görünürlük

Ölçüm kayıtlarını toplayan bir defter sınıfı, üç farklı görünürlük biçimini bir arada
gösterir:

```typescript
interface Olcum {
  readonly kimlik: string;
  deger: number;
}

class OlcumDefteri {
  private kayitlar: Olcum[] = [];
  #gizliSayac = 0;

  constructor(readonly ad: string) {}

  ekle(olcum: Olcum): void {
    this.kayitlar.push(olcum);
    this.#gizliSayac += 1;
  }

  get sayi(): number {
    return this.kayitlar.length;
  }
}

const defter = new OlcumDefteri("kazan-2");
defter.ekle({ kimlik: "s-01", deger: 21.4 });
console.log(defter.ad, defter.sayi);
```

Dosya `defter.ts` adıyla `tsc --strict --target es2022 defter.ts` komutuyla derlendiğinde
üretilen JavaScript:

```javascript
"use strict";
class OlcumDefteri {
    ad;
    kayitlar = [];
    #gizliSayac = 0;
    constructor(ad) {
        this.ad = ad;
    }
    ekle(olcum) {
        this.kayitlar.push(olcum);
        this.#gizliSayac += 1;
    }
    get sayi() {
        return this.kayitlar.length;
    }
}
const defter = new OlcumDefteri("kazan-2");
defter.ekle({ kimlik: "s-01", deger: 21.4 });
console.log(defter.ad, defter.sayi);
```

`node defter.js` çıktısı `kazan-2 1`dir. Çıktıda üç şey görülür.

**`private` silinmiştir.** `private kayitlar` alanı, derlenmiş kodda `kayitlar` olarak
durur ve dışarıdan okunabilir. **`#gizliSayac` durmaktadır.** Bu, TypeScript'e değil
JavaScript'e ait bir özelliktir ve çalışma zamanında zorunlu tutulur.

**`constructor(readonly ad: string)` yazımı kod üretmiştir.** Buna **parametre özelliği**
(parameter property) denir: yapıcı parametresine bir görünürlük veya `readonly` belirteci
konduğunda derleyici, aynı adlı bir alan bildirir ve atamayı ekler. Numaralandırma gibi,
bu da tip silme ilkesinin istisnasıdır.

Farkın sonucu, derlenmiş sınıf doğrudan JavaScript'ten kullanıldığında gözlenir. Aşağıdaki
dosya, yukarıda üretilen sınıf gövdesini alıp üç sorgu ekler:

```javascript
class OlcumDefteri {
    ad;
    kayitlar = [];
    #gizliSayac = 0;
    constructor(ad) {
        this.ad = ad;
    }
    ekle(olcum) {
        this.kayitlar.push(olcum);
        this.#gizliSayac += 1;
    }
    get sayi() {
        return this.kayitlar.length;
    }
}

const defter = new OlcumDefteri("kazan-2");
defter.ekle({ kimlik: "s-01", deger: 21.4 });

console.log(Object.keys(defter));
console.log(defter.kayitlar);
console.log(defter.gizliSayac);
```

```text
[ 'ad', 'kayitlar' ]
[ { kimlik: 's-01', deger: 21.4 } ]
undefined
```

`kayitlar` alanı hem sayılabilir hem okunabilir; `#gizliSayac` ise `gizliSayac` adıyla
erişilmeye çalışıldığında `undefined` verir, çünkü öyle bir genel alan yoktur.

Derleme anında ikisi de korunur, ama farklı tanılarla:

```typescript
class OlcumDefteri {
  private kayitlar: number[] = [];
  #sayac = 0;

  ekle(deger: number): void {
    this.kayitlar.push(deger);
    this.#sayac += 1;
  }
}

const defter = new OlcumDefteri();
defter.ekle(21.4);
console.log(defter.kayitlar);
console.log(defter.#sayac);
```

```text
i1.ts(13,20): error TS2341: Property 'kayitlar' is private and only accessible within class 'OlcumDefteri'.
i1.ts(14,20): error TS18013: Property '#sayac' is not accessible outside class 'OlcumDefteri' because it has a private identifier.
```

Seçim ölçütü şudur: **görünürlük bir tasarım kuralıysa `private`, bir güvenlik sınırıysa
`#`.** Ekip içinde yanlış kullanımı önlemek için `private` yeterlidir ve derleme anında
yakalar. Kütüphane sınırında, denetlenmeyen kodun erişmemesi gerekiyorsa `#` kullanılır.

`protected` belirteci de yalnızca derleme anında geçerlidir ve alanı sınıfın kendisine ve
alt sınıflarına açar.

#### Soyut Sınıflar

**Soyut sınıf** (abstract class), gerçekleştirilmemiş üyeler bildirebilen ve doğrudan
örneklenemeyen sınıftır. Ölçüm kaynağı sözleşmesi, ortak bir davranış da taşıyacaksa
arayüz yerine soyut sınıfla yazılır:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

abstract class OlcumKaynagi {
  abstract readonly ad: string;
  protected abstract sonraki(): Olcum | null;

  tumu(): Olcum[] {
    const toplanan: Olcum[] = [];
    let kayit = this.sonraki();
    while (kayit !== null) {
      toplanan.push(kayit);
      kayit = this.sonraki();
    }
    return toplanan;
  }
}

class DiziKaynagi extends OlcumKaynagi {
  readonly ad = "dizi";
  private sira = 0;

  constructor(private readonly kayitlar: readonly Olcum[]) {
    super();
  }

  protected sonraki(): Olcum | null {
    return this.sira < this.kayitlar.length ? this.kayitlar[this.sira++] : null;
  }
}

const kaynak = new DiziKaynagi([
  { kimlik: "s-01", deger: 21.4 },
  { kimlik: "s-02", deger: 22.1 },
]);
console.log(kaynak.ad, kaynak.tumu().length);
```

Çıktı `dizi 2`dir.

Yapı, iki tür üyeyi ayırır. `sonraki` yöntemi soyuttur: her kaynak farklı gerçekleştirir.
`tumu` yöntemi ise somuttur ve `sonraki` üzerine kurulmuş ortak davranışı taşır. Alt sınıf
yalnızca değişen parçayı yazar.

Soyut sınıfı doğrudan örneklemeye çalışan bir satır, aynı soyut sınıfın kısaltılmış
tanımıyla ayrı bir dosyada (`i2.ts`) denenirse derleyici şu tanıyı verir:

```typescript
// i2.ts — soyut sınıfı örneklemek
interface Olcum {
  kimlik: string;
  deger: number;
}

abstract class OlcumKaynagi {
  abstract readonly ad: string;
  protected abstract sonraki(): Olcum | null;
}

const olmaz = new OlcumKaynagi();
```

```text
i2.ts(12,15): error TS2511: Cannot create an instance of an abstract class.
```

`abstract` belirteci de silinir — derlenmiş çıktıda sıradan bir sınıf kalır ve JavaScript
tarafından örneklenebilir. Yasak, derleme anına aittir.

#### `implements`

Bir sınıfın belirli bir sözleşmeyi karşıladığı `implements` ile bildirilir:

```typescript
interface OlcumKaynagi {
  readonly ad: string;
  sonraki(): number | null;
}

class BosKaynak implements OlcumKaynagi {
  readonly ad = "bos";
}
```

```text
i4.ts(6,7): error TS2420: Class 'BosKaynak' incorrectly implements interface 'OlcumKaynagi'.
  Property 'sonraki' is missing in type 'BosKaynak' but required in type 'OlcumKaynagi'.
```

`implements` bir **sınama** bildirimidir, bir kalıtım değil. Sınıfa hiçbir üye eklemez;
yalnızca sözleşmeye uygunluğu denetler ve tanıyı sınıfın bildirim satırında verir. Bu
bildirim olmasaydı sınıf yine derlenirdi, uyumsuzluk ancak kullanım yerinde ortaya çıkardı.

`implements` yazımı tümüyle silinir; derlenmiş çıktıda izi kalmaz.

#### Soyut Sınıf mı, Arayüz mü

İki araç örtüşür; ölçüt şudur:

| Ölçüt | Arayüz | Soyut sınıf |
|---|---|---|
| Ortak gerçekleştirim taşır | Hayır | Evet |
| Çalışma zamanı kodu üretir | Hayır | Evet |
| Bir tip birden çoğunu karşılar | Evet | Hayır (tek üst sınıf) |
| Durum (alan değeri) taşır | Hayır | Evet |

**Yalnızca sözleşme gerekiyorsa arayüz.** Silinir, birden çoğu birlikte kullanılabilir ve
gerçekleştirimi bağlamaz.

**Sözleşmeyle birlikte ortak davranış gerekiyorsa soyut sınıf.** Yukarıdaki `tumu` yöntemi
bunun örneğidir: tek yerde yazılmış, tüm kaynaklarda geçerli bir algoritma.

Programlama Temelleri kursunda tanıtılan **sınıf değişmezi** kavramı burada tip düzeyinde
destek bulur: `private` alanlar üzerinde tutulan bir değişmez, dışarıdan bozulamaz — çünkü
alanlara yalnızca sınıfın kendi yöntemleri erişebilir ve derleyici bunu sınar.

#### Özet

- Sınıf alanlarının ve yöntemlerinin tipleri silinir; `private` ve `protected` yalnızca
  derleme anında geçerlidir, `#` ile bildirilen alanlar çalışma zamanında da korunur.
- Parametre özelliği (`constructor(readonly ad: string)`) kod üretir: alan bildirimi ve
  atama derleyici tarafından eklenir.
- Soyut sınıf, gerçekleştirilmemiş üyeler bildirir ve doğrudan örneklenemez; `abstract`
  belirteci derlenmiş çıktıda kalmaz.
- `implements` bir sınama bildirimidir; sınıfa üye eklemez, tanıyı bildirim satırında verir
  ve tümüyle silinir.
- Yalnızca sözleşme için arayüz, sözleşmeyle birlikte ortak davranış için soyut sınıf
  seçilir.

#### Sonraki Adım

Sınıf yöntemleri ve tek başına duran fonksiyonlar, bu derste yalnızca kullanıldı;
tiplerinin nasıl yazıldığı ele alınmadı. Bir fonksiyonun tipi parametre ve dönüş
tiplerinden ibaret değildir: seçmeli parametreler, kalan parametreler, aşırı yükleme ve
`this` bağlamı da tipin parçasıdır. Sonraki ders fonksiyon tiplerini ayrıntılı kuracak.

### Fonksiyon Tipleri

Source: https://academia.sh/tr/kurslar/typescript/fonksiyon-tipleri

Fonksiyon tipi yazımı, seçmeli ve kalan parametreler, void dönüş tipinin özel davranışı, parametre sayısı kuralı, aşırı yükleme ve this parametresi.

Önceki derste sınıf yöntemleri ve tek başına duran fonksiyonlar kullanıldı ama tiplerinin
nasıl yazıldığı ele alınmadı. Bir fonksiyonun tipi parametre ve dönüş tiplerinden ibaret
değildir: seçmeli parametreler, kalan parametreler, çağrı biçimine göre değişen dönüş
tipleri ve `this` bağlamı da tipin parçasıdır.

Bu ders bu bileşenleri sırayla kurar ve ölçüm kaydını okuyan bir ayrıştırıcı üzerinde
uygular.

#### Fonksiyon Tipi Yazımı

Bir fonksiyon tipi, parametre listesi ve ok işaretinden sonra dönüş tipiyle yazılır:

```typescript
type Olcum = { kimlik: string; deger: number };
type Suzgec = (olcum: Olcum) => boolean;

function suz(kayitlar: readonly Olcum[], olcut: Suzgec): Olcum[] {
  return kayitlar.filter(olcut);
}

const pozitif: Suzgec = (o) => o.deger > 0;
const kayitlar: Olcum[] = [
  { kimlik: "s-01", deger: 21.4 },
  { kimlik: "s-02", deger: -3 },
];

console.log(suz(kayitlar, pozitif).length);

const yanlis: Suzgec = (o) => o.deger;
```

```text
j1.ts(16,31): error TS2322: Type 'number' is not assignable to type 'boolean'.
```

`pozitif` bildiriminde `o` parametresine tip yazılmamıştır; tip, bağlamsal tipleme yoluyla
`Suzgec` tanımından gelir. Son satır ise dönüş tipi uymadığı için reddedilir —
JavaScript'te doğruluk değeri olarak kullanılabilecek bir sayı, `boolean` beklenen yere
konamaz.

Fonksiyon tipindeki parametre adları (`olcum`) belgeleme amaçlıdır; uyumlulukta rol
oynamazlar. Önemli olan sıra ve tiptir.

#### Seçmeli, Varsayılan ve Kalan Parametreler

```typescript
type Olcum = { kimlik: string; deger: number; not?: string };

function olustur(
  kimlik: string,
  deger: number,
  birim: string = "C",
  ...etiketler: string[]
): string {
  return `${kimlik}=${deger}${birim} [${etiketler.join(",")}]`;
}

console.log(olustur("s-01", 21.4));
console.log(olustur("s-02", 101325, "Pa", "kazan", "vardiya-3"));

function yaz(olcum: Olcum): string {
  return olcum.not.trim();
}
console.log(yaz({ kimlik: "s-03", deger: 1 }));
```

```text
j2.ts(16,10): error TS18048: 'olcum.not' is possibly 'undefined'.
```

`birim` parametresinin varsayılan değeri vardır; tipi `string` olarak çıkarılır ve
çağrıda atlanabilir. `etiketler` kalan parametredir; tipi bir dizidir ve her zaman
tanımlıdır — atlanırsa boş dizi olur.

Son tanı, seçmeli alanların bedelini gösterir: `not?: string` bildirimi alanın tipini
`string | undefined` yapar ve `undefined` üzerinde `trim` çağrılamaz. Derleyici, atlanan
denetimi bildirir. Düzeltme daraltmadır: `olcum.not?.trim() ?? ""` ya da açık bir `if`
bloğu.

Bu davranış `strictNullChecks` seçeneğine bağlıdır. Seçenek kapalıyken `undefined` her
tipe atanabilir ve bu tanı gelmez — kapalı bir yapılandırmada seçmeli alanlar hiçbir
güvence taşımaz.

#### `void` Dönüş Tipinin Özel Davranışı

`void`, "dönüş değeri kullanılmayacak" anlamına gelir. Beklenenden gevşek bir kuralı
vardır:

```typescript
type Olcum = { kimlik: string; deger: number };
type Kaydedici = (olcum: Olcum) => void;

const kayitlar: Olcum[] = [];
const ekle: Kaydedici = (olcum) => kayitlar.push(olcum);

ekle({ kimlik: "s-01", deger: 21.4 });
console.log(kayitlar.length);

const sonuc = ekle({ kimlik: "s-02", deger: 22.1 });
console.log(sonuc.toFixed(0));
```

```text
j3.ts(11,19): error TS2339: Property 'toFixed' does not exist on type 'void'.
```

Beşinci satır hata **vermez**. `kayitlar.push(olcum)` ifadesi bir sayı döndürür, oysa
`Kaydedici` tipi `void` dönüş bildirir. Kural şudur: **dönüş değeri olan bir fonksiyon,
`void` dönen bir tipe atanabilir.** Gerekçe, `void` bildiriminin "hiçbir şey döndürme"
değil "döndürdüğün şey kullanılmayacak" demesidir.

Bunun sonucu on birinci satırda görülür: çağrı yerinden bakıldığında dönüş tipi `void`tir
ve üzerinde işlem yapılamaz. Değer gerçekte vardır, tip sistemi onu görmeyi reddeder.

Bu kural, geri çağrı alan fonksiyonların kullanımını kolaylaştırır. `forEach` yönteminin
geri çağrısı `void` döner; her geri çağrının dönüşsüz yazılması zorunlu olsaydı,
`(o) => kayitlar.push(o)` gibi kısa yazımlar reddedilirdi.

#### Parametre Sayısı Kuralı

Fonksiyon uyumluluğunda parametre sayısı tek yönlü çalışır:

```typescript
type Olcum = { kimlik: string; deger: number };
type Geri = (olcum: Olcum, sira: number) => void;

const yalnizKayit: Geri = (olcum) => console.log(olcum.kimlik);
yalnizKayit({ kimlik: "s-01", deger: 21.4 }, 0);

const fazla: Geri = (olcum, sira, ek: string) => console.log(ek);
```

```text
j4.ts(7,7): error TS2322: Type '(olcum: any, sira: any, ek: string) => void' is not assignable to type 'Geri'.
  Target signature provides too few arguments. Expected 3 or more, but got 2.
j4.ts(7,22): error TS7006: Parameter 'olcum' implicitly has an 'any' type.
j4.ts(7,29): error TS7006: Parameter 'sira' implicitly has an 'any' type.
```

**Daha az** parametre alan bir fonksiyon, daha çok parametre bildiren bir tipe atanabilir;
fazladan gelen argümanları yok sayar. **Daha çok** parametre alan bir fonksiyon
atanamaz — çağıran o argümanı sağlamayacaktır.

Bu, JavaScript'in çağrı kuralının doğrudan yansımasıdır: fazladan argüman hataya yol
açmaz, eksik argüman `undefined` olur. Tip sistemi ilkine izin verir, ikincisini engeller.

Son iki tanıya dikkat: yedinci satırda `olcum` ve `sira` parametreleri bağlamsal tiplerini
kaybetmiştir. Atama başarısız olduğu için bağlam kurulamamış, parametreler örtük `any`
olarak kalmıştır. Tek bir hatanın birden çok tanı üretmesi sık görülür; kaynak tanı ilk
olandır.

#### Aşırı Yükleme

Bir fonksiyon, argümanın tipine göre farklı dönüş tipleri veriyorsa **aşırı yükleme**
(overloading) ile bildirilir:

```typescript
type Olcum = { kimlik: string; deger: number };

function birKayit(ham: string): Olcum | null {
  const parcalar = ham.split("=");
  if (parcalar.length !== 2) {
    return null;
  }
  const deger = Number(parcalar[1]);
  return Number.isFinite(deger) ? { kimlik: parcalar[0], deger } : null;
}

function oku(ham: string): Olcum | null;
function oku(ham: readonly string[]): Olcum[];
function oku(ham: string | readonly string[]): Olcum | Olcum[] | null {
  if (typeof ham === "string") {
    return birKayit(ham);
  }
  return ham.map(birKayit).filter((o): o is Olcum => o !== null);
}

const tek = oku("s-01=21.4");
const cok = oku(["s-01=21.4", "bozuk", "s-02=22.1"]);

console.log(tek?.deger);
console.log(cok.length, cok.map((o) => o.kimlik).join(","));
```

Çıktı:

```text
21.4
2 s-01,s-02
```

Üç imza vardır ama yalnızca ikisi çağrılabilir. İlk iki satır **çağrı imzalarıdır**;
üçüncüsü **gerçekleştirim imzasıdır** ve dışarıdan görünmez. `tek` bildiriminin tipi
`Olcum | null`, `cok` bildiriminin tipi `Olcum[]`tir — tek bir birleşim tipi yerine, çağrı
biçimine göre kesin tip.

Gerçekleştirim imzasının çağrılamaması, birleşim tipli bir argümanla denendiğinde görünür.
Aynı tanımlarla, birleşim tipli bir argüman ayrı bir dosyada (`j8.ts`) denenirse:

```typescript
// j8.ts — birleşim tipli argümanla çağrı
type Olcum = { kimlik: string; deger: number };

function birKayit(ham: string): Olcum | null {
  const parcalar = ham.split("=");
  if (parcalar.length !== 2) {
    return null;
  }
  const deger = Number(parcalar[1]);
  return Number.isFinite(deger) ? { kimlik: parcalar[0], deger } : null;
}

function oku(ham: string): Olcum | null;
function oku(ham: readonly string[]): Olcum[];
function oku(ham: string | readonly string[]): Olcum | Olcum[] | null {
  if (typeof ham === "string") {
    return birKayit(ham);
  }
  return ham.map(birKayit).filter((o): o is Olcum => o !== null);
}

declare const belirsiz: string | readonly string[];
const belirsizSonuc = oku(belirsiz);
console.log(belirsizSonuc);
```

```text
j8.ts(23,27): error TS2769: No overload matches this call.
  The last overload gave the following error.
    Argument of type 'string | readonly string[]' is not assignable to parameter of type 'readonly string[]'.
      Type 'string' is not assignable to type 'readonly string[]'.
```

Tanı yalnızca son çağrı imzasının neden uymadığını yazar; gerçekleştirim imzasından hiç
söz etmez. Çağıranın bu durumda yapması gereken, argümanı çağrıdan önce
daraltmaktır.

Aşırı yükleme yalnızca tip düzeyinde bir bildirimdir. Derlenmiş çıktıda tek bir fonksiyon
kalır ve dallanmayı gerçekleştirim gövdesi yapar — `typeof ham === "string"` sınaması
buradadır.

#### `this` Parametresi

Bir fonksiyonun hangi bağlamda çağrılacağı da tiplenebilir. İlk parametre adı `this`
olduğunda, o parametre gerçek bir parametre değil bağlam bildirimidir:

```typescript
type Defter = { ad: string; kayitlar: number[] };

function ozet(this: Defter): string {
  return `${this.ad}: ${this.kayitlar.length}`;
}

const defter: Defter & { ozet: () => string } = {
  ad: "kazan-2",
  kayitlar: [21.4, 22.1],
  ozet,
};

console.log(defter.ozet());
```

Çıktı `kazan-2: 2`dir. Fonksiyonu nesneden koparıp çağıran bir satır, aynı tanımlarla ayrı
bir dosyada (`baglam.ts`) denenirse:

```typescript
// baglam.ts — bağlamı kaybolan çağrı
type Defter = { ad: string; kayitlar: number[] };

function ozet(this: Defter): string {
  return `${this.ad}: ${this.kayitlar.length}`;
}

console.log(ozet());
```

```text
baglam.ts(8,13): error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'Defter'.
```

JavaScript'te Nesneler ve Fonksiyonlar kursunda kurulan `this` bağlanma kuralları burada
tip düzeyinde sınanır: nesneden koparılarak çağrılan bir yöntemin bağlamı kaybolur ve
derleyici bunu bildirir.

`this` parametresi de tümüyle silinir. Yukarıdaki dosyanın derlenmiş çıktısında fonksiyon
imzası boştur:

```javascript
function ozet() {
    return `${this.ad}: ${this.kayitlar.length}`;
}
```

#### Özet

- Fonksiyon tipi parametre listesi ve dönüş tipiyle yazılır; parametre adları
  uyumlulukta rol oynamaz.
- Seçmeli alan ve parametreler tipe `undefined` ekler; kullanılmadan önce daraltılmaları
  gerekir.
- Dönüş değeri olan bir fonksiyon `void` dönen tipe atanabilir, ancak çağrı yerinde dönüş
  değeri kullanılamaz.
- Daha az parametre alan fonksiyon daha çok parametreli tipe atanabilir; tersi
  atanamaz.
- Aşırı yüklemede çağrı imzaları dışarıdan görünür, gerçekleştirim imzası görünmez;
  dallanma çalışma zamanında gövdede yapılır.
- `this` parametresi bağlamı tipler ve tümüyle silinir.

#### Sonraki Adım

Bu konudaki örneklerin hiçbirinde bir değer, uyduğu tipin adını yazmadı: nesne sabitleri
arayüzlere, fonksiyonlar fonksiyon tiplerine yalnızca **biçimleri** uyduğu için kabul
edildi. Bu, TypeScript'in tip uyumluluğunun temel kuralıdır ve beklenmedik sonuçları
vardır. Sonraki ders yapısal tiplemenin kurallarını, fazlalık özellik denetimini ve
yapısal uyumluluğun aşılması gereken durumları ele alacak.

### Yapısal Tip Uyumluluğu

Source: https://academia.sh/tr/kurslar/typescript/yapisal-tip-uyumlulugu

Ördek tiplemesinin kuralları, nesne sabitlerinde fazlalık özellik denetimi, fonksiyon parametrelerinde yön kuralı ve yapısal uyumluluğu kırmanın yolları.

Bu konudaki örneklerin hiçbirinde bir değer, uyduğu tipin adını yazmadı. Nesne sabitleri
arayüzlere, fonksiyonlar fonksiyon tiplerine yalnızca **biçimleri** uyduğu için kabul
edildi.

Bu, TypeScript'in tip uyumluluğunun temel kuralıdır. **Yapısal tipleme** (structural
typing) — halk arasındaki adıyla **ördek tiplemesi** (duck typing) — bir değerin bir tipe
uyup uymadığını, bildirdiği ada değil taşıdığı üyelere bakarak belirler. Karşıtı **adsal
tiplemedir** (nominal typing): tip uyumu ancak açıkça bildirildiğinde geçerlidir.

Bu ders yapısal tiplemenin kurallarını, çıkardığı sorunları ve gerektiğinde nasıl
kırılacağını ele alır.

#### Temel Kural

Bir tip, hedef tipin gerektirdiği tüm üyeleri taşıyorsa uyumludur. Fazladan üye taşıması
uyumu bozmaz:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

const uyusan = { kimlik: "s-01", deger: 21.4, konum: "kazan-2" };
const dolayli: Olcum = uyusan;
console.log(dolayli.kimlik);

const dogrudan: Olcum = { kimlik: "s-02", deger: 22.1, konum: "kazan-3" };
```

```text
k1.ts(10,56): error TS2353: Object literal may only specify known properties, and 'konum' does not exist in type 'Olcum'.
```

Yedinci satır geçerlidir: `uyusan` nesnesi `Olcum`un istediği iki alanı taşır, üçüncü alan
fazlalıktır ve yok sayılır. Onuncu satır ise aynı biçimdeki bir nesneyi doğrudan
yazdığında reddedilir.

Fark, **fazlalık özellik denetimidir** (excess property check). Derleyici, doğrudan bir
tipe atanan **taze nesne sabitlerinde** bilinmeyen alanları hata sayar. Gerekçe pratiktir:
doğrudan yazılan bir sabitteki fazladan alan neredeyse her zaman yazım hatasıdır ya da
yanlış anlaşılmış bir sözleşmedir. Değişken üzerinden gelen bir nesnede ise fazladan alan
olağandır — nesne başka bir amaç için de kullanılıyor olabilir.

Denetimin dar kapsamı bilinçlidir ve tam bir koruma sağlamaz. Bir ara değişkene atamak
denetimi atlatır; bu, kuralın kaçamağı değil kapsamıdır.

#### Adın Rolü Yok

Yapısal tiplemenin en dikkat çekici sonucu, birbiriyle ilgisiz iki tipin uyumlu
olabilmesidir:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

interface Urun {
  kimlik: string;
  deger: number;
}

const olcum: Olcum = { kimlik: "s-01", deger: 21.4 };
const urun: Urun = olcum;
console.log(urun.deger);
```

Bu dosya hatasız derlenir. `Olcum` ve `Urun` farklı kavramlardır, `deger` alanları farklı
şeyler ölçer; tip sistemine göre aynı tiptir.

Bu davranış bir kusur değil, tasarım kararıdır. TypeScript var olan JavaScript'in üzerine
eklenir; JavaScript'te nesneler bildirilmiş tiplerle değil sahip oldukları özelliklerle
dolaşır. Bir kütüphaneye "şu alanları taşıyan bir nesne" verilebiliyorsa, tip sisteminin
de aynı esnekliği tanıması gerekir. Yapısal tipleme, önceki derste arayüzü hiçbir sınıfın
adını yazmadan gerçekleştirebilmenin de nedenidir.

Bedeli, anlamı aynı olmayan tiplerin karışabilmesidir. Bu dersin ilerleyen bölümünde buna
karşı bir araç kurulacaktır.

#### Fonksiyonlarda Yön Kuralı

Fonksiyon tiplerinde uyumluluk, parametreler için **ters** yönde çalışır:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

interface KonumluOlcum extends Olcum {
  konum: string;
}

type Isleyici = (olcum: Olcum) => void;

const konumaGore = (olcum: KonumluOlcum): void => console.log(olcum.konum);
const isleyici: Isleyici = konumaGore;

isleyici({ kimlik: "s-01", deger: 21.4 });
```

```text
k4.ts(13,7): error TS2322: Type '(olcum: KonumluOlcum) => void' is not assignable to type 'Isleyici'.
  Types of parameters 'olcum' and 'olcum' are incompatible.
    Property 'konum' is missing in type 'Olcum' but required in type 'KonumluOlcum'.
```

Gerekçe on beşinci satırda görülür: `isleyici` tipi, `Olcum` alan bir fonksiyon vaat eder;
çağıran `konum` alanı olmayan bir kayıt gönderir; `konumaGore` ise o alana erişir. Atama
kabul edilseydi program çalışma zamanında `undefined` üzerinde işlem yapardı.

Kural şu biçimde özetlenir: bir fonksiyon, **daha geniş** parametre kabul eden bir tipe
atanabilir; **daha dar** parametre isteyen bir fonksiyon atanamaz. Dönüş tipinde ise yön
düzdür — daha dar bir dönüş tipi, daha geniş bir dönüş tipi bekleyen yere uyar.

Bu kural yalnızca ok yazımıyla bildirilen fonksiyon tiplerinde uygulanır. **Yöntem
sözdizimiyle** bildirilen üyelerde derleyici her iki yöne de izin verir:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

interface KonumluOlcum extends Olcum {
  konum: string;
}

interface IsleyiciYontem {
  isle(olcum: Olcum): void;
}

const nesne: IsleyiciYontem = {
  isle(olcum: KonumluOlcum): void {
    console.log(olcum.konum);
  },
};

nesne.isle({ kimlik: "s-01", deger: 21.4 });
```

Bu dosya tip denetiminden **geçer** ve çalıştırıldığında `undefined` yazar.

Bu, tip sisteminin bilinçli olarak bıraktığı bir boşluktur. Nedeni dizi yöntemlerinin
tiplenmesidir: `Array<T>` üzerindeki yöntemler yöntem sözdizimiyle yazılmıştır ve katı
kural uygulansaydı sık kullanılan pek çok kalıp reddedilirdi. İlk derste sözü edilen "tam
güvenilir olmama" durumunun somut bir örneğidir.

Pratik sonuç: geri çağrı tiplerini **ok yazımıyla** bildirmek daha güvenlidir.

#### Yapısal Uyumluluğu Kırmak

İki nedenle uyumluluğun kırılması istenebilir: farklı anlam taşıyan aynı biçimli tipleri
ayırmak ve bir değerin belirli bir doğrulamadan geçtiğini tip düzeyinde kaydetmek.

Sınıflarda `private` veya `protected` üye bulunması uyumluluğu kendiliğinden kırar:

```typescript
class OlcumDefteri {
  private kayitlar: number[] = [];
}

class UrunDefteri {
  private kayitlar: number[] = [];
}

const defter: OlcumDefteri = new UrunDefteri();
console.log(defter);
```

```text
k3.ts(9,7): error TS2322: Type 'UrunDefteri' is not assignable to type 'OlcumDefteri'.
  Types have separate declarations of a private property 'kayitlar'.
```

İki alan aynı adı ve aynı tipi taşır; yine de uyumsuzdur, çünkü özel üyeler yalnızca
**aynı bildirimden** geldiklerinde uyumlu sayılır. Bu, sınıflara adsal tiplemeye yakın bir
davranış kazandırır.

Nesne tipleri için karşılık gelen teknik **markalı tiptir** (branded type): tipe, çalışma
zamanında var olmayan bir alan eklenir.

```typescript
declare const marka: unique symbol;

type SensorKimligi = string & { readonly [marka]: "sensor" };

function sensorKimligi(ham: string): SensorKimligi {
  if (!/^s-\d{2}$/.test(ham)) {
    throw new Error(`gecersiz sensor kimligi: ${ham}`);
  }
  return ham as SensorKimligi;
}

function olcumOku(kimlik: SensorKimligi): string {
  return `${kimlik} okundu`;
}

console.log(olcumOku(sensorKimligi("s-01")));

const dizgi: string = sensorKimligi("s-02");
console.log(dizgi.toUpperCase());
```

Çıktı:

```text
s-01 okundu
S-02
```

Dosyanın sonuna, bir boş satırdan sonra `olcumOku("s-03");` satırı eklenirse:

```text
marka2.ts(21,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'SensorKimligi'.
  Type 'string' is not assignable to type '{ readonly [marka]: "sensor"; }'.
```

Yapı üç parçadan oluşur. `declare const marka: unique symbol` bildirimi, benzersiz bir tip
düzeyi anahtar üretir — `declare` yazıldığı için çalışma zamanında karşılığı yoktur.
`SensorKimligi` tipi, dizgi ile o anahtarı taşıyan bir nesnenin kesişimidir; hiçbir
gerçek dizgi bu alanı taşımaz, dolayısıyla tipe yalnızca tip iddiasıyla girilebilir.
`sensorKimligi` fonksiyonu bu iddiayı tek bir yerde, doğrulamadan **sonra** yapar.

Sonuç, bir kanıtın tip sisteminde taşınmasıdır: `SensorKimligi` tipinde bir değer, biçim
denetiminden geçmiş demektir. `dizgi` bildiriminin geçerli olması da tasarımın parçasıdır
— markalı tip yine bir dizgidir ve dizgi işlemleri kullanılabilir; ters yön kapalıdır.

Bu kalıp, ilk derste konan kurala uyar: doğrulama çalışma zamanında yapılır, tip sistemi
yalnızca sonucu kaydeder.

#### Özet

- Yapısal tiplemede uyumluluk, bildirilen ada değil taşınan üyelere bakılarak belirlenir;
  ilgisiz iki tip aynı biçimdeyse uyumludur.
- Fazlalık özellik denetimi yalnızca doğrudan yazılan nesne sabitlerinde uygulanır; ara
  değişkenden gelen nesnelerde uygulanmaz.
- Fonksiyon parametrelerinde uyum ters yönde çalışır: daha geniş parametre kabul eden
  fonksiyon, daha dar parametre bildiren tipe atanabilir.
- Yöntem sözdizimiyle bildirilen üyelerde bu kural gevşetilmiştir ve çalışma zamanı hatası
  üretebilen bir boşluk bırakır.
- Sınıflardaki özel üyeler uyumluluğu kırar; nesne tipleri için markalı tip kullanılır ve
  bu kalıp bir doğrulamanın yapıldığını tip düzeyinde kaydeder.

#### Sonraki Adım

Bu konudaki tiplerin tümü belirli ve sabitti: `Olcum` kayıtları tutan bir defter, `Olcum`
alan bir süzgeç. Oysa defter yapısının kayıt tipinden bağımsız olması gerekir — aynı kod
ölçümleri, uyarıları veya olayları toplamalıdır. Sonraki konu tip parametrelerini
tanıtacak ve bu bağımsızlığı, tip güvencesini kaybetmeden kuracak.

## İleri Tipler

Genellikler ve kısıtlar, tip daraltma, eşlenmiş ve koşullu tipler, şablon dizgi tipleri, yardımcı tipler ve özyinelemeli tip tanımları.

### Genellikler

Source: https://academia.sh/tr/kurslar/typescript/genellikler

Tip parametreleriyle biçimden bağımsız yapılar yazma, tip argümanı çıkarımı, kısıtlar, keyof kısıtı, varsayılan tip parametreleri ve genelliğin kötüye kullanımı.

Önceki konudaki tiplerin tümü belirliydi: `Olcum` kayıtları tutan bir defter, `Olcum` alan
bir süzgeç. Oysa defterin yapısı kayıt tipinden bağımsızdır — aynı kod ölçümleri,
uyarıları veya olayları toplamalıdır.

Bunu sağlamanın iki kötü yolu vardır. Her kayıt tipi için ayrı bir defter yazmak kodu
çoğaltır; deftere `any[]` kullandırmak ise tip güvencesini kaldırır. **Genellik**
(generic), üçüncü yolu verir: yapıyı bir **tip parametresiyle** yazmak ve tipi kullanım
yerinde vermek.

#### Tip Parametreli Yapılar

Tip parametresi, bildirim adından sonra açılı ayraç içinde yazılır ve gövde boyunca bir
tip gibi kullanılır:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

class Defter<T> {
  private kayitlar: T[] = [];

  ekle(kayit: T): void {
    this.kayitlar.push(kayit);
  }

  sonuncu(): T | undefined {
    return this.kayitlar.at(-1);
  }

  get sayi(): number {
    return this.kayitlar.length;
  }
}

const olcumler = new Defter<Olcum>();
olcumler.ekle({ kimlik: "s-01", deger: 21.4 });
console.log(olcumler.sonuncu()?.deger, olcumler.sayi);

const uyarilar = new Defter<string>();
uyarilar.ekle("sensor yanit vermiyor");
console.log(uyarilar.sonuncu()?.toUpperCase());
```

Çıktı:

```text
21.4 1
SENSOR YANIT VERMIYOR
```

Tek bir sınıf iki farklı kayıt tipiyle kullanıldı ve her kullanımda tip bilgisi korundu:
`olcumler.sonuncu()` çağrısı `Olcum | undefined`, `uyarilar.sonuncu()` çağrısı
`string | undefined` verir. Dosyanın sonuna, bir boş satırdan sonra
`olcumler.ekle("s-02");` satırı eklenirse:

```text
m1.ts(30,15): error TS2345: Argument of type 'string' is not assignable to parameter of type 'Olcum'.
```

`any[]` çözümü bu tanıyı üretmezdi. Genellik, ortak yapıyı paylaşırken tip güvencesini
kaybetmemenin yoludur.

Dönüş tipinin `T | undefined` olması da tasarımın parçası: `at(-1)` çağrısı boş bir dizide
`undefined` verir ve tip bunu kaydeder. Çağıran, `?.` yazımıyla bu olasılığı ele almak
zorundadır.

#### Tip Argümanı Çıkarımı

Tip argümanı çoğu zaman yazılmaz; derleyici argümanlardan çıkarır:

```typescript
function ilk<T>(kayitlar: readonly T[]): T | undefined {
  return kayitlar[0];
}

const sayi = ilk([21.4, 22.1]);
const metin = ilk(["s-01", "s-02"]);
console.log(sayi?.toFixed(1), metin?.toUpperCase());
```

Çıktı `21.4 S-01`dir.

`ilk<number>` ve `ilk<string>` yazımlarına gerek kalmamıştır. `sayi` bildiriminin tipi
`number | undefined`, `metin` bildiriminin tipi `string | undefined` olarak çıkarılır ve
her birinde o tipin yöntemleri kullanılabilir.

Çıkarımın çalışması için tip parametresinin **argümanlarda geçmesi** gerekir. Yalnızca
dönüş tipinde geçen bir tip parametresi çıkarılamaz; bu durumun tehlikesi dersin sonunda
ele alınacaktır.

#### Kısıtlar

Tip parametresi hiçbir şey bilinmeyen bir tiptir; üzerinde işlem yapılamaz. Belirli üyelere
erişmek için parametreye **kısıt** (constraint) yazılır:

```typescript
interface Kimlikli {
  kimlik: string;
}

function kimlikleriTopla<T extends Kimlikli>(kayitlar: readonly T[]): string[] {
  return kayitlar.map((k) => k.kimlik);
}

console.log(kimlikleriTopla([{ kimlik: "s-01", deger: 21.4 }]));
console.log(kimlikleriTopla([{ ad: "kazan-2" }]));
```

```text
m4.ts(10,32): error TS2353: Object literal may only specify known properties, and 'ad' does not exist in type 'Kimlikli'.
```

`T extends Kimlikli` bildirimi iki iş yapar: gövdede `k.kimlik` erişimini olanaklı kılar
ve uymayan argümanları çağrı yerinde reddeder. Dokuzuncu satırın kabul edilmesi de önemli
— `deger` alanı fazlalıktır, kısıt yalnızca en az gerekliliği belirtir.

Kısıt yazmadan `k.kimlik` yazılsaydı derleyici erişimi reddederdi: `T` herhangi bir tip
olabilir, dizgi de olabilir, sayı da.

#### `keyof` ile Kısıt

Kısıtlar başka tip parametrelerine de başvurabilir. En kullanışlı biçimi, bir tipin alan
adlarını veren `keyof` işleciyle kurulur:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
  birim: "C" | "Pa" | "%";
}

function alan<T, A extends keyof T>(kayit: T, ad: A): T[A] {
  return kayit[ad];
}

const kayit: Olcum = { kimlik: "s-01", deger: 21.4, birim: "C" };
const d: number = alan(kayit, "deger");
const b: "C" | "Pa" | "%" = alan(kayit, "birim");
console.log(d, b);

alan(kayit, "konum");
```

```text
m5.ts(16,13): error TS2345: Argument of type '"konum"' is not assignable to parameter of type 'keyof Olcum'.
```

Üç yeni yazım var. `keyof T`, `T` tipinin alan adlarının sabit tiplerinden oluşan bir
birleşimdir — burada `"kimlik" | "deger" | "birim"`. `A extends keyof T` kısıtı, ikinci
argümanı bu adlarla sınırlar. `T[A]` ise **indeksli erişim tipidir** (indexed access
type): `A` adlı alanın tipini verir.

Sonuç, alan adına göre değişen bir dönüş tipidir. `alan(kayit, "deger")` çağrısının tipi
`number`, `alan(kayit, "birim")` çağrısının tipi `"C" | "Pa" | "%"`tir. Aşırı yükleme
yazmadan, alan sayısı kadar farklı imza elde edilmiştir.

#### Varsayılan Tip Parametresi

Tip parametrelerine varsayılan değer verilebilir. Sınırda doğrulamanın sonucunu taşıyan
tip bunun tipik kullanımıdır:

```typescript
type Sonuc<D, H = string> =
  | { durum: "basarili"; deger: D }
  | { durum: "hatali"; hata: H };

interface Olcum {
  kimlik: string;
  deger: number;
}

function oku(ham: string): Sonuc<Olcum> {
  const parcalar = ham.split("=");
  if (parcalar.length !== 2) {
    return { durum: "hatali", hata: `bicim gecersiz: ${ham}` };
  }
  const deger = Number(parcalar[1]);
  if (!Number.isFinite(deger)) {
    return { durum: "hatali", hata: `sayi degil: ${parcalar[1]}` };
  }
  return { durum: "basarili", deger: { kimlik: parcalar[0], deger } };
}

function yaz(sonuc: Sonuc<Olcum>): string {
  return sonuc.durum === "basarili"
    ? `${sonuc.deger.kimlik}=${sonuc.deger.deger}`
    : `hata: ${sonuc.hata}`;
}

console.log(yaz(oku("s-01=21.4")));
console.log(yaz(oku("bozuk")));
console.log(yaz(oku("s-02=abc")));
```

Çıktı:

```text
s-01=21.4
hata: bicim gecersiz: bozuk
hata: sayi degil: abc
```

`Sonuc<Olcum>` yazımında ikinci tip argümanı verilmemiş, varsayılan `string`
kullanılmıştır. Hata tipinin ayrıntılanması gerektiğinde `Sonuc<Olcum, HataKodu>` yazılır
ve `yaz` fonksiyonu dışındaki kod değişmez.

Bu tip, ilk derste konan sınır kuralının olgunlaşmış hâlidir. `oku` fonksiyonu ne bir
istisna atar ne de `null` döndürür; başarısızlığı **tipin bir üyesi** olarak taşır ve
çağıranın onu ele almasını derleyici zorunlu kılar. Ayırt edici birleşim burada bir hata
yönetimi aracına dönüşmüştür.

#### Genelliğin Kötüye Kullanımı

Genellik her yerde iyileştirme sağlamaz. İki karşı örnek vardır.

**Tek kez geçen tip parametresi.** `function yazdir<T>(deger: T): void` imzasında `T`
yalnızca bir parametrede geçer ve hiçbir bağıntı kurmaz; `unknown` yazmak aynı işi daha
açık yapar. Genelliğin değeri, iki nokta arasında bir **bağ** kurmasındadır: girdi tipi
ile çıktı tipi, ya da iki parametre.

**Yalnızca dönüş tipinde geçen tip parametresi.** Bu, tip iddiasının gizlenmiş hâlidir:

```typescript
function tehlikeli<T>(ham: unknown): T {
  return ham as T;
}

const kimlik = tehlikeli<string>(42);
try {
  console.log(kimlik.toUpperCase());
} catch (e) {
  console.log((e as Error).message);
}
```

Dosya tip denetiminden **geçer**; çalıştırıldığında ise `TypeError` oluşur ve `catch` bloğu
iletisini yazdırır:

```text
kimlik.toUpperCase is not a function
```

Çağıran `T` olarak ne yazarsa yazsın derleyici kabul eder, çünkü gövdedeki `as T` yazımı
denetimi susturur. Bu imza, bir doğrulama vaat ediyor gibi görünüp hiçbir şey
doğrulamaz — `any` döndürmekten daha tehlikelidir, çünkü tip güvenli görünür.

Kural: **tip parametresi en az iki yerde geçmelidir.** Yalnızca dönüş tipinde geçiyorsa,
imza `unknown` döndürmeli ve çağıran daraltmayı kendisi yapmalıdır.

#### Özet

- Tip parametresi, bir yapının biçimini kayıt tipinden bağımsız yazmayı sağlar ve
  `any` çözümünün aksine tip güvencesini korur.
- Tip argümanları çoğu zaman çağrı argümanlarından çıkarılır; çıkarım için parametrenin
  argüman listesinde geçmesi gerekir.
- Kısıt (`T extends K`), tip parametresi üzerinde işlem yapılabilmesini sağlar ve uymayan
  argümanları çağrı yerinde reddeder.
- `keyof T` alan adlarının birleşimini, `T[A]` indeksli erişim tipini verir; ikisi birlikte
  alan adına göre değişen dönüş tipleri kurar.
- Varsayılan tip parametresi yaygın durumu kısaltır; ayırt edici birleşimle kurulan sonuç
  tipi, başarısızlığı tipin bir üyesi olarak taşır.
- Tip parametresi en az iki yerde geçmelidir; yalnızca dönüş tipinde geçen bir parametre,
  gizlenmiş bir tip iddiasıdır.

#### Sonraki Adım

Bu derste birkaç kez bir birleşim tipinin üzerinde sınama yapıldı ve derleyici tipi tek bir
üyeye indirdi. Bu indirgemenin kuralları henüz sıralanmadı: hangi sınamalar kanıt sayılır,
kanıt ne kadar uzağa taşınır, kendi yazdığınız doğrulama fonksiyonunun sonucu derleyiciye
nasıl bildirilir? Sonraki ders daraltmayı ayrıntılı ele alacak.

### Tip Daraltma

Source: https://academia.sh/tr/kurslar/typescript/tip-daraltma

Denetim akışı çözümlemesiyle tip daraltma; typeof, in ve instanceof koruyucuları, doğruluk daraltmasının tuzağı, tip yüklemleri, doğrulama fonksiyonları ve daraltmanın kaybolduğu durumlar.

Önceki derslerde birkaç kez bir birleşim tipi üzerinde sınama yapıldı ve derleyici tipi
tek bir üyeye indirdi. Bu indirgemenin kuralları henüz sıralanmadı.

**Daraltma** (narrowing), derleyicinin bir değerin tipini denetim akışındaki sınamalara
bakarak küçültmesidir. Arkasındaki mekanizmaya **denetim akışı çözümlemesi** (control
flow analysis) denir: derleyici, programın her noktasında hangi koşulların kanıtlanmış
olduğunu izler. Bu ders
hangi sınamaların kanıt sayıldığını, kendi doğrulamanızın derleyiciye nasıl bildirileceğini
ve kanıtın nerede kaybolduğunu ele alır.

#### Yerleşik Koruyucular

**Tip koruyucusu** (type guard), bir değerin tipini daraltan sınamadır. En yaygını
`typeof` işlecidir:

```typescript
function bicimle(deger: string | number | null): string {
  if (deger === null) {
    return "-";
  }
  if (typeof deger === "number") {
    return deger.toFixed(1);
  }
  return deger.trim();
}

console.log(bicimle(21.4), bicimle("  s-01  "), bicimle(null));
```

Çıktı `21.4 s-01 -`tir.

Üç dal, üç daraltma adımıdır. `deger === null` sınaması eşitlikle daraltır; ilk `return`
sonrası kalan tip `string | number`dır. `typeof deger === "number"` sınamasından sonra
gövdede tip `number` olur ve `toFixed` çağrılabilir. Son satıra ulaşıldığında kalan tek
olasılık `string`tir; `trim` çağrısı için ek bir sınama gerekmez.

Son adım dikkat ister: derleyici **erken dönüşleri** izler. `return` deyimiyle biten bir
dal, sonraki kodda o olasılığı eler. Programlama Temelleri kursunda tanıtılan koruma
cümlesi kalıbı, tip düzeyinde de kazanç sağlar.

Nesne tipleri için iki koruyucu daha vardır. `in` işleci alan varlığına bakar:

```typescript
type SayisalOlcum = { kimlik: string; deger: number };
type MetinselOlcum = { kimlik: string; metin: string };

function yaz(kayit: SayisalOlcum | MetinselOlcum): string {
  if ("deger" in kayit) {
    return kayit.deger.toFixed(1);
  }
  return kayit.metin.trim();
}

console.log(yaz({ kimlik: "s-01", deger: 21.4 }));
console.log(yaz({ kimlik: "s-02", metin: "  bozuk  " }));
```

Çıktı:

```text
21.4
bozuk
```

`instanceof` işleci ise sınıf örneklerini ayırır:

```typescript
class DosyaKaynagi {
  constructor(readonly yol: string) {}
}

class AgKaynagi {
  constructor(readonly adres: string) {}
}

function tanim(kaynak: DosyaKaynagi | AgKaynagi): string {
  if (kaynak instanceof DosyaKaynagi) {
    return `dosya: ${kaynak.yol}`;
  }
  return `ag: ${kaynak.adres}`;
}

console.log(tanim(new DosyaKaynagi("/veri/olcum.jsonl")));
console.log(tanim(new AgKaynagi("10.0.0.7:9000")));
```

Çıktı:

```text
dosya: /veri/olcum.jsonl
ag: 10.0.0.7:9000
```

Bu üçünün ortak özelliği, hepsinin **çalışma zamanında gerçekten çalışan** sınamalar
olmasıdır. Daraltma, tip sisteminin bir varsayımı değil; koddaki bir sınamanın tip
düzeyine yansımasıdır.

#### Doğruluk Daraltmasının Tuzağı

Bir değerin doğruluk değerine bakmak da daraltır, ancak beklenenden fazlasını eler:

```typescript
function etiket(deger: number | undefined): string {
  if (!deger) {
    return "olcum yok";
  }
  return deger.toFixed(1);
}

console.log(etiket(21.4));
console.log(etiket(0));
console.log(etiket(undefined));
```

Çıktı:

```text
21.4
olcum yok
olcum yok
```

Bu dosya tip denetiminden geçer, ama ikinci çıktı yanlıştır: `0` geçerli bir ölçümdür.
JavaScript Temelleri kursunda kurulan yanlış sayılan değerler listesi burada devreye
girer — `0`, `""` ve `NaN` da yanlış sayılır ve `!deger` sınaması onları da eler.

Tip sistemi bu hatayı yakalamaz, çünkü daraltma doğru yapılmıştır: `!deger` bloğundan
sonra tip gerçekten `number`dır. Hata tipte değil, sınamanın seçiminde.

Kural: **`undefined` ve `null` için eşitlik sınaması yazılır.** `if (deger === undefined)`
ya da `if (deger == null)` — ikincisi hem `null` hem `undefined` durumunu eler ve bu, iki
eşittirin savunulabilir tek kullanımıdır.

#### Tip Yüklemleri

Doğrulama ayrı bir fonksiyona taşındığında daraltma kaybolur: derleyici, `boolean` dönen
bir fonksiyonun neyi kanıtladığını bilmez. **Tip yüklemi** (type predicate), bu bilgiyi
imzada taşır:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
  birim: "C" | "Pa" | "%";
}

function olcumMu(deger: unknown): deger is Olcum {
  if (typeof deger !== "object" || deger === null) {
    return false;
  }
  const aday = deger as Record<string, unknown>;
  return (
    typeof aday.kimlik === "string" &&
    typeof aday.deger === "number" &&
    (aday.birim === "C" || aday.birim === "Pa" || aday.birim === "%")
  );
}

function oku(ham: string): string {
  const cozulen: unknown = JSON.parse(ham);
  if (!olcumMu(cozulen)) {
    return "gecersiz kayit";
  }
  return `${cozulen.kimlik}=${cozulen.deger}${cozulen.birim}`;
}

console.log(oku('{"kimlik":"s-01","deger":21.4,"birim":"C"}'));
console.log(oku('{"kimlik":"s-02","deger":"22.1","birim":"C"}'));
console.log(oku('{"kimlik":"s-03","deger":22.1,"birim":"K"}'));
```

Çıktı:

```text
s-01=21.4C
gecersiz kayit
gecersiz kayit
```

Dönüş tipi `boolean` değil `deger is Olcum` yazılmıştır. Bu, derleyiciye şunu söyler:
"bu fonksiyon `true` döndürdüğünde, argümanının `Olcum` olduğunu kabul et." Sonuç, `oku`
içinde `cozulen` değerinin `unknown` olmaktan çıkıp `Olcum` olmasıdır.

Bu, kursun eksenindeki tek noktada toplanır: doğrulama çalışma zamanında yapılır, tip
yüklemi bunun **kanıt olarak** kaydedilmesini sağlar. Model artık dış veriden güvenle
kurulabilir.

Yüklemin bir bedeli vardır. Derleyici, gövdenin gerçekten iddia edileni doğruladığını
sınamaz:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

function olcumMu(deger: unknown): deger is Olcum {
  return typeof deger === "object";
}

const aday: unknown = null;
try {
  if (olcumMu(aday)) {
    console.log(aday.deger.toFixed(1));
  }
} catch (e) {
  console.log((e as Error).message);
}
```

Dosya tip denetiminden geçer; çalıştırıldığında `TypeError` oluşur ve `catch` bloğu
iletisini yazdırır:

```text
Cannot read properties of null (reading 'deger')
```

Gövde `typeof null === "object"` sonucunu `true` verir ve yanlış bir kanıt üretir. Tip
yüklemi, `as` gibi bir **borçtur**: doğruluğundan yazan sorumludur. Bu nedenle yüklem
gövdeleri kısa tutulur ve sınanır.

#### Doğrulama Fonksiyonları

Yüklemin ikinci biçimi, sınamayı geçemeyen değerde hata atar:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

function olcumOldugunuDogrula(deger: unknown): asserts deger is Olcum {
  if (
    typeof deger !== "object" ||
    deger === null ||
    typeof (deger as Record<string, unknown>).kimlik !== "string" ||
    typeof (deger as Record<string, unknown>).deger !== "number"
  ) {
    throw new TypeError("olcum kaydi degil");
  }
}

const cozulen: unknown = JSON.parse('{"kimlik":"s-01","deger":21.4}');
olcumOldugunuDogrula(cozulen);
console.log(cozulen.kimlik, cozulen.deger.toFixed(1));
```

Çıktı `s-01 21.4`tür.

`asserts deger is Olcum` bildirimi, çağrının **normal döndüğü** durumda tipin daralmasını
sağlar. Bir `if` bloğuna gerek kalmaz; çağrıdan sonraki tüm kodda `cozulen` bir `Olcum`dur.

İki biçim arasındaki seçim, geçersiz verinin beklenen bir durum olup olmadığına bağlıdır.
Programlama Temelleri kursundaki Tip Dönüşümü dersinde aynı ayrım yapılmıştı: kullanıcı
girdisinde geçersizlik olağandır ve yüklemle ele alınır; programın kendi ürettiği verinin
geçersizliği istisnaidir ve doğrulama fonksiyonu uygundur.

Bir kısıt vardır: doğrulama fonksiyonunun çağrıldığı ad, açıkça tiplenmiş bir bildirim
olmalıdır. Fonksiyon önce tipsiz bir sabite atanır ve o sabit üzerinden çağrılırsa
derleyici `TS2775` tanısını verir: doğrulama çağrılarında hedefteki her adın açık tip
açıklaması taşıması gerekir.

#### Daraltmanın Kaybolduğu Yer

Denetim akışı çözümlemesi, değerin sınamadan sonra değişmediğini varsayabildiği sürece
çalışır. Kapanış içinde bu varsayım geçersizdir:

```typescript
interface Olcum {
  kimlik: string;
  son?: number;
}

function yaz(kayit: Olcum): void {
  if (kayit.son !== undefined) {
    console.log(kayit.son.toFixed(1));
    [1, 2].forEach(() => {
      console.log(kayit.son.toFixed(1));
    });
  }
}

yaz({ kimlik: "s-01", son: 21.4 });
```

```text
n6.ts(10,19): error TS18048: 'kayit.son' is possibly 'undefined'.
```

Sekizinci satır geçerlidir, onuncu satır değildir. Fark, geri çağrının **ne zaman**
çalışacağının bilinmemesidir: `forEach` senkron çalışır ama derleyici bunu bilemez ve
arada `kayit.son` alanı silinmiş olabilir. Aynı sorun zamanlayıcılarda ve eşzamansız
kodda daha da belirgindir.

Çözüm, daraltılmış değeri yerel bir sabite almaktır:

```typescript
interface Olcum {
  kimlik: string;
  son?: number;
}

function yaz(kayit: Olcum): void {
  const son = kayit.son;
  if (son !== undefined) {
    console.log(son.toFixed(1));
    [1, 2].forEach(() => {
      console.log(son.toFixed(1));
    });
  }
}

yaz({ kimlik: "s-01", son: 21.4 });
```

`const` ile bağlanan bir ad değişemez; derleyici daraltmayı kapanış içine taşıyabilir.
Çıktı üç satır `21.4`tür.

#### Özet

- Daraltma, denetim akışındaki sınamaların tip düzeyine yansımasıdır; `typeof`, `in`,
  `instanceof` ve eşitlik sınamaları yerleşik koruyuculardır.
- Doğruluk sınaması `0` ve `""` gibi geçerli değerleri de eler; `null` ve `undefined` için
  eşitlik sınaması yazılır.
- Tip yüklemi (`deger is T`), ayrı bir fonksiyona taşınmış doğrulamanın sonucunu derleyiciye
  bildirir; gövdenin doğruluğu sınanmaz, yüklem bir borçtur.
- Doğrulama fonksiyonu (`asserts deger is T`), geçersiz değerde hata atar ve normal
  döndüğünde tipi daraltır.
- Daraltma kapanış içine taşınmaz; daraltılan değer yerel bir sabite alınarak bu sınır
  aşılır.

#### Sonraki Adım

Ölçüm kaydının değişik biçimleri şimdiye kadar elle yazıldı: salt okunur sürüm, alanları
seçmeli olan güncelleme sürümü, yalnızca birkaç alanı taşıyan özet sürüm. Her biri asıl
tipin bir dönüşümüdür ve tip değiştiğinde birlikte güncellenmeleri gerekir. Sonraki ders
bu dönüşümleri tip düzeyinde hesaplayan eşlenmiş tipleri kuracak.

### Eşlenmiş Tipler

Source: https://academia.sh/tr/kurslar/typescript/eslenmis-tipler

Var olan bir tipten yeni tip üretme; alan değiştiricilerinin eklenmesi ve kaldırılması, anahtar yeniden eşlemesi, alan süzme ve eşlemenin dizi ile demetler üzerindeki davranışı.

Ölçüm kaydının değişik biçimleri şimdiye kadar elle yazıldı: arşivlenmiş kayıtlar için
salt okunur bir sürüm, güncelleme isteği için alanları seçmeli bir sürüm, listeleme için
yalnızca birkaç alanı taşıyan bir özet.

Her biri asıl tipin bir dönüşümüdür. Elle yazıldıklarında asıl tip değiştiğinde birlikte
güncellenmeleri gerekir ve bu, unutulmaya elverişli bir bakım borcudur. **Eşlenmiş tip**
(mapped type), dönüşümü tip düzeyinde hesaplar: alan listesi tek yerde durur, türevler
kendiliğinden güncellenir.

#### Temel Yazım

Eşlenmiş tip, bir anahtar kümesi üzerinde dolaşan bir döngüye benzer:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
  birim: "C" | "Pa" | "%";
}

type SaltOkunur<T> = { readonly [A in keyof T]: T[A] };
type Yamalanabilir<T> = { [A in keyof T]?: T[A] };

const arsivKayit: SaltOkunur<Olcum> = {
  kimlik: "s-01",
  deger: 21.4,
  birim: "C",
};
console.log(arsivKayit.deger);

const yama: Yamalanabilir<Olcum> = { deger: 22.1 };
console.log(yama.deger, yama.kimlik);
```

Çıktı:

```text
21.4
22.1 undefined
```

Dosyanın sonuna, bir boş satırdan sonra `arsivKayit.deger = 22.1;` satırı eklenirse:

```text
o2.ts(20,12): error TS2540: Cannot assign to 'deger' because it is a read-only property.
```

Yazımın parçaları şunlardır. `[A in keyof T]` bölümü, `keyof T` birleşiminin her üyesini
sırayla `A` adına bağlar — Programlama Temelleri kursundaki `for` döngüsünün tip
düzeyindeki karşılığı. İki noktadan sonraki `T[A]`, o alanın asıl tipidir. Başa yazılan
`readonly` ve ada eklenen `?`, her alana uygulanan **değiştiricilerdir**.

`Yamalanabilir<Olcum>` tipinin bütün alanları seçmeli olduğu için `{ deger: 22.1 }` sabiti
kabul edilir; `yama.kimlik` erişiminin tipi `string | undefined`dır ve çıktıda `undefined`
görünür.

Bu iki dönüşümün karşılığı standart kitaplıkta zaten bulunur (`Readonly` ve `Partial`);
Yardımcı Tipler dersinde ele alınacaklardır. Burada elle yazılmalarının nedeni,
mekanizmanın görünür olmasıdır.

#### Değiştirici Kaldırma

Değiştiriciler eklenebildiği gibi kaldırılabilir de. Kaldırma, değiştiricinin önüne eksi
işareti konarak yazılır:

```typescript
interface Olcum {
  readonly kimlik: string;
  readonly deger?: number;
}

type Cozulmus<T> = { -readonly [A in keyof T]-?: T[A] };

const tam: Cozulmus<Olcum> = { kimlik: "s-01", deger: 21.4 };
tam.deger = 22.1;
console.log(tam.deger.toFixed(1));
```

Çıktı `22.1`dir. Aynı tanımlarla, zorunlu alanı vermeyen bir atama ayrı bir dosyada
(`o4.ts`) denenirse:

```typescript
// o4.ts — Cozulmus tipinde eksik alan
interface Olcum {
  readonly kimlik: string;
  readonly deger?: number;
}

type Cozulmus<T> = { -readonly [A in keyof T]-?: T[A] };

const eksik: Cozulmus<Olcum> = { kimlik: "s-02" };
```

```text
o4.ts(9,7): error TS2741: Property 'deger' is missing in type '{ kimlik: string; }' but required in type 'Cozulmus<Olcum>'.
```

`Olcum` tipinde `kimlik` salt okunur, `deger` hem salt okunur hem seçmeliydi.
`Cozulmus<Olcum>` tipinde ikisi de yazılabilir ve zorunludur. `tam.deger.toFixed(1)`
çağrısının daraltma gerektirmemesi bunun sonucudur: alan artık `number`dır,
`number | undefined` değil.

Bu dönüşümün kullanım yeri, kısmi bir kaydın tamamlanmış hâlini temsil etmektir.
Varsayılan değerler uygulandıktan sonra elde edilen kayıt için ayrı bir tip yazmak yerine,
asıl tipten hesaplanır.

#### Anahtar Yeniden Eşlemesi

`as` yazımı, üretilen alanın adını değiştirmeyi sağlar:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
  birim: "C" | "Pa" | "%";
}

type Okuyucular<T> = {
  [A in keyof T as `oku${Capitalize<string & A>}`]: () => T[A];
};

const okuyucu: Okuyucular<Olcum> = {
  okuKimlik: () => "s-01",
  okuDeger: () => 21.4,
  okuBirim: () => "C",
};

console.log(okuyucu.okuDeger().toFixed(1), okuyucu.okuKimlik());
```

Çıktı `21.4 s-01`dir.

Üretilen tip, her alan için bir okuyucu fonksiyon bildirir ve dönüş tipini asıl alandan
alır: `okuDeger` çağrısı `number`, `okuBirim` çağrısı `"C" | "Pa" | "%"` verir. Ad
üretiminde kullanılan şablon dizgi yazımı ile `Capitalize` dönüşümü sonraki dersin
konusudur; burada yalnızca adın hesaplanabildiği görülmelidir.

Yeniden eşlemenin ikinci ve daha güçlü kullanımı, bir alanı **elemektir**. Üretilen ad
`never` olduğunda o alan çıktıya girmez:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
  birim: "C" | "Pa" | "%";
  zaman: number;
}

type SayisalAlanlar<T> = {
  [A in keyof T as T[A] extends number ? A : never]: T[A];
};

const sayisal: SayisalAlanlar<Olcum> = { deger: 21.4, zaman: 1706000000000 };
console.log(sayisal.deger + sayisal.zaman);
```

Çıktı `1706000000021.4`tür. Aynı tanımlarla, elenmiş bir alanı da veren bir atama ayrı
bir dosyada (`o7.ts`) denenirse:

```typescript
// o7.ts — SayisalAlanlar tipinde fazla alan
interface Olcum {
  kimlik: string;
  deger: number;
  birim: "C" | "Pa" | "%";
  zaman: number;
}

type SayisalAlanlar<T> = {
  [A in keyof T as T[A] extends number ? A : never]: T[A];
};

const yanlis: SayisalAlanlar<Olcum> = { deger: 21.4, zaman: 1, kimlik: "s-01" };
```

```text
o7.ts(13,64): error TS2353: Object literal may only specify known properties, and 'kimlik' does not exist in type 'SayisalAlanlar<Olcum>'.
```

Sonuç tipi yalnızca `deger` ve `zaman` alanlarını taşır; `kimlik` ve `birim` elenmiştir.
`T[A] extends number ? A : never` yazımı bir **koşullu tiptir** ve sonraki derslerden
birinin konusudur; burada işlevi, alan tipine bakarak adı tutmak veya `never` üretip
elemektir.

Bu tip, ölçüm kaydının sayısal özetini çıkaran kodun imzasında kullanılabilir. Kayda yeni
bir sayısal alan eklendiğinde özet tipi kendiliğinden büyür; dizgi bir alan eklendiğinde
etkilenmez.

#### Diziler ve Demetler Üzerinde Eşleme

`keyof T` üzerinde dolaşan bir eşleme diziye uygulandığında beklenmedik bir şey **olmaz**:
sonuç yine bir dizidir.

```typescript
type SaltOkunur<T> = { readonly [A in keyof T]: T[A] };

const degerler: SaltOkunur<number[]> = [21.4, 22.1];
const aralik: SaltOkunur<[alt: number, ust: number]> = [-40, 85];

console.log(degerler.length, degerler[0], aralik[1]);
console.log(degerler.map((d) => d * 2));
```

Çıktı:

```text
2 21.4 85
[ 42.8, 44.2 ]
```

`SaltOkunur<number[]>` tipi `readonly number[]`, `SaltOkunur<[number, number]>` tipi salt
okunur bir demettir. Dizi yöntemleri (`map`, `length`, indeksli erişim) kullanılabilir
durumda kalmış, değiştirme yöntemleri kalkmıştır:

```typescript
type SaltOkunur<T> = { readonly [A in keyof T]: T[A] };

const degerler: SaltOkunur<number[]> = [21.4, 22.1];
degerler.push(23.0);
```

```text
u4.ts(4,10): error TS2339: Property 'push' does not exist on type 'readonly number[]'.
```

Bu davranış, eşlemenin kaynağı **doğrudan** `keyof T` olduğunda geçerlidir. Derleyici bu
biçimi tanır ve dizi ile demetleri kendi türlerinde tutar. Kaynak başka bir ifadeyle
yazıldığında tanıma bozulur:

```typescript
type EslemA<T> = { readonly [A in keyof T]: T[A] };
type EslemB<T> = { readonly [A in keyof T & string]: T[A] };

declare const a: EslemA<number[]>;
declare const b: EslemB<number[]>;

const x: null = a;
const y: null = b;
```

```text
u11.ts(7,7): error TS2322: Type 'readonly number[]' is not assignable to type 'null'.
u11.ts(8,7): error TS2322: Type 'EslemB<number[]>' is not assignable to type 'null'.
```

Tanıların farkı belirleyicidir. `EslemA<number[]>` tipi `readonly number[]` olarak
çözülmüştür — derleyici sonucu bir dizi olarak yazıyor. `EslemB<number[]>` ise
çözülmemiş bir eşleme olarak kalmıştır; dizi yapısı kaybolmuş, yerine dizinin özellik
adlarını taşıyan sıradan bir nesne tipi geçmiştir.

Pratik kural: **bir tipin yapısını korumak isteniyorsa eşleme kaynağı `keyof T` olarak
yazılır.** Anahtar süzmek gerekiyorsa süzme, `as` yeniden eşlemesiyle yapılır — kaynak
`keyof T` kalır, ad hesabı sağ tarafta olur.

#### Herhangi Bir Birleşim Üzerinde Eşleme

Eşleme kaynağının `keyof T` olması zorunlu değildir; herhangi bir dizgi birleşimi
kullanılabilir:

```typescript
type Birim = "C" | "Pa" | "%";

type Araliklar = {
  [B in Birim]: [alt: number, ust: number];
};

const araliklar: Araliklar = {
  C: [-40, 85],
  Pa: [0, 200000],
  "%": [0, 100],
};

console.log(araliklar.C[1], araliklar["%"][1]);
```

Çıktı `85 100`dür. Aynı tanımlarla, `"%"` anahtarını vermeyen bir atama ayrı bir
dosyada (`o9.ts`) denenirse:

```typescript
// o9.ts — Araliklar tipinde eksik anahtar
type Birim = "C" | "Pa" | "%";

type Araliklar = {
  [B in Birim]: [alt: number, ust: number];
};

const eksik: Araliklar = { C: [-40, 85], Pa: [0, 200000] };
```

```text
o9.ts(8,7): error TS2741: Property '"%"' is missing in type '{ C: [number, number]; Pa: [number, number]; }' but required in type 'Araliklar'.
```

Bu, Arayüzler dersinde eleştirilen indeks imzasının doğru alternatifidir. İndeks imzası
"her dizgi anahtar geçerlidir" der ve olmayan anahtara erişimi hata saymazdı. Eşlenmiş tip
ise anahtar kümesini **tam olarak** belirler: eksik bir anahtar tanı verir, fazladan bir
anahtar da öyle.

Değer kümesi `Birim` birleşimine yeni bir birim eklendiğinde, aralık tablosunu güncellemek
zorunlu hâle gelir. Ayırt edici birleşimde `never` ile kurulan tükenmişlik denetiminin,
veri tabloları için karşılığıdır.

#### Özet

- Eşlenmiş tip, bir anahtar kümesi üzerinde dolaşarak yeni bir nesne tipi üretir; türev
  tipler asıl tiple birlikte güncellenir.
- `readonly` ve `?` değiştiricileri eşleme sırasında eklenebilir; `-readonly` ve `-?`
  yazımlarıyla kaldırılabilir.
- `as` yazımı üretilen alanın adını hesaplar; ad `never` olduğunda alan çıktıdan elenir.
- Eşleme kaynağı `keyof T` olmak zorunda değildir; herhangi bir dizgi birleşimi üzerinde
  eşleme yapılabilir.
- Eşleme kaynağı doğrudan `keyof T` olduğunda dizi ve demetler kendi türlerinde kalır;
  kaynak başka bir ifadeyle yazıldığında bu yapı kaybolur.
- Sabit anahtar kümesi için eşlenmiş tip, indeks imzasından güvenlidir: eksik ve fazla
  anahtarların ikisi de bildirilir.

#### Sonraki Adım

Önceki iki örnekte `T[A] extends number ? A : never` biçiminde bir yazım geçti ve tip
düzeyinde bir dallanma yaptı. Bu, tip sisteminin hesaplama yeteneğinin merkezindeki
yapıdır. Sonraki ders koşullu tipleri, dağılım kurallarını ve bir tipin içinden parça
çıkaran `infer` yazımını ele alacak.

### Koşullu Tipler

Source: https://academia.sh/tr/kurslar/typescript/kosullu-tipler

Tip düzeyinde dallanma, birleşimler üzerinde dağılım kuralı ve dağılımın kapatılması, infer ile tip parçası çıkarma ve koşullu dönüş tiplerinin sınırı.

Önceki derste `T[A] extends number ? A : never` biçiminde bir yazım geçti ve tip düzeyinde
bir dallanma yaptı. **Koşullu tip** (conditional type), tip sisteminin hesaplama
yeteneğinin merkezindeki yapıdır: bir tipin başka bir tipe atanabilir olup olmadığına göre
iki tipten birini seçer.

Yazımı üçlü işlecin tip düzeyindeki karşılığıdır:

$$
T \text{ extends } U \;?\; X : Y
$$

Bu ders koşulun nasıl değerlendirildiğini, birleşimler üzerindeki özel davranışını,
`infer` ile parça çıkarmayı ve yapının sınırlarını ele alır.

#### Tip Düzeyinde Dallanma

```typescript
type SayiMi<T> = T extends number ? "sayi" : "sayi degil";

const a: SayiMi<number> = "sayi";
const b: SayiMi<string> = "sayi degil";
console.log(a, b);

const c: SayiMi<number> = "sayi degil";
```

```text
p1.ts(7,7): error TS2322: Type '"sayi degil"' is not assignable to type '"sayi"'.
```

`SayiMi<number>` tipi `"sayi"`, `SayiMi<string>` tipi `"sayi degil"` olarak çözülür.
Yedinci satırın tanısı, çözümün gerçekten yapıldığını gösterir: derleyici `SayiMi<number>`
yerine `"sayi"` yazarak hatayı bildirir.

`extends` sözcüğü buradaki anlamıyla kalıtım değil **atanabilirlik** sınamasıdır: "`T`,
`U`'nun beklendiği yere konabilir mi?" Yapısal Tip Uyumluluğu dersinde kurulan kurallar burada
uygulanır.

#### Dağılım

Koşullu tipin tip parametresi bir birleşimse, koşul her üye için **ayrı ayrı**
değerlendirilir ve sonuçların birleşimi alınır. Buna **dağılım** (distribution) denir:

```typescript
type SayiMi<T> = T extends number ? "sayi" : "sayi degil";
type Dagilmayan<T> = [T] extends [number] ? "sayi" : "sayi degil";

declare const dagilan: SayiMi<number | string>;
declare const kapali: Dagilmayan<number | string>;

const x: null = dagilan;
const y: null = kapali;
```

```text
p2.ts(7,7): error TS2322: Type '"sayi" | "sayi degil"' is not assignable to type 'null'.
  Type '"sayi"' is not assignable to type 'null'.
p2.ts(8,7): error TS2322: Type '"sayi degil"' is not assignable to type 'null'.
```

Tanılar iki tipi de açıkça yazıyor. `SayiMi<number | string>` tipi `"sayi" | "sayi degil"`
olmuş; koşul her üye için ayrı değerlendirilmiş, sonuçlar birleştirilmiştir. Üyelerin
tanıdaki sırası derleyicinin kendi sırasıdır. `Dagilmayan<number | string>` ise tek bir
sonuç vermiştir: `"sayi degil"`.

Dağılımı kapatan şey, tip parametresinin **demet içine alınmasıdır**. `[T] extends [U]`
yazımında sınanan artık `T` değil, tek elemanlı bir demettir; birleşim üyelerine ayrılmaz
ve koşul bir kez değerlendirilir.

İki davranıştan hangisinin isteneceği amaca bağlıdır. Bir birleşimin her üyesini
dönüştürmek isteniyorsa dağılım gerekir. "Bu tipin **tümü** şu koşulu sağlıyor mu?"
sorusuna yanıt aranıyorsa kapatılmalıdır — aksi hâlde `number | string` birleşimi için
"kısmen sayı" gibi anlamsız bir sonuç çıkar.

Dağılım yalnızca **çıplak** tip parametrelerinde geçerlidir: koşulun sol tarafı doğrudan
`T` olmalıdır. `T[]`, `[T]` ya da `{ deger: T }` yazımlarında dağılım olmaz.

#### Süzme: `Cikar`

Dağılımın en yaygın kullanımı, bir birleşimden üye elemektir. Elenecek üye için `never`
üretilir; `never` bir birleşimde yer kaplamaz:

```typescript
type Cikar<T, U> = T extends U ? never : T;

type Birim = "C" | "Pa" | "%";
type OlculebilirBirim = Cikar<Birim, "%">;

const birimler: OlculebilirBirim[] = ["C", "Pa"];
console.log(birimler.join(","));
```

Çıktı `C,Pa`dır. Aynı tanımlarla, elenen üyeyi atamaya çalışan satır ayrı bir dosyada
(`p5.ts`) denenirse:

```typescript
// p5.ts — Cikar ile elenen üye
type Cikar<T, U> = T extends U ? never : T;

type Birim = "C" | "Pa" | "%";
type OlculebilirBirim = Cikar<Birim, "%">;

const yanlis: OlculebilirBirim = "%";
```

```text
p5.ts(7,7): error TS2322: Type '"%"' is not assignable to type 'OlculebilirBirim'.
```

Hesabın adımları şöyledir: koşul üç üye için ayrı ayrı değerlendirilir; `"C"` ve `"Pa"`
için `"%"` tipine atanamadıklarından kendileri, `"%"` için ise `never` üretilir. Sonuç
`"C" | "Pa" | never` yani `"C" | "Pa"`dır.

Bu dönüşümün standart kitaplıktaki karşılığı `Exclude`tur ve Yardımcı Tipler dersinde ele
alınacaktır.

#### `infer` ile Parça Çıkarma

Koşulun sol tarafında `infer` sözcüğüyle bir tip değişkeni bildirilebilir. Derleyici,
eşleştirme sırasında o değişkene karşılık gelen tipi bulur:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

type ElemanTipi<T> = T extends readonly (infer E)[] ? E : never;

declare const eleman: ElemanTipi<Olcum[]>;
declare const dizgiEleman: ElemanTipi<readonly string[]>;
declare const dizisiz: ElemanTipi<number>;

const a: null = eleman;
const b: null = dizgiEleman;
const c: null = dizisiz;
```

```text
p3.ts(12,7): error TS2322: Type 'Olcum' is not assignable to type 'null'.
p3.ts(13,7): error TS2322: Type 'string' is not assignable to type 'null'.
```

`ElemanTipi<Olcum[]>` tipi `Olcum`, `ElemanTipi<readonly string[]>` tipi `string` olmuştur.
On dördüncü satırın tanı vermemesi de bilgi taşır: `ElemanTipi<number>` tipi `never`dir ve
`never` her tipe — `null` dahil — atanabilir.

Aynı teknik, kursun sonuç tipinden yük tipini çıkarmak için kullanılır:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

type Sonuc<D, H = string> =
  | { durum: "basarili"; deger: D }
  | { durum: "hatali"; hata: H };

type BasariTipi<S> = S extends { durum: "basarili"; deger: infer D } ? D : never;
type HataTipi<S> = S extends { durum: "hatali"; hata: infer H } ? H : never;

declare const basari: BasariTipi<Sonuc<Olcum>>;
declare const hata: HataTipi<Sonuc<Olcum, number>>;

const a: null = basari;
const b: null = hata;
```

```text
p4.ts(16,7): error TS2322: Type 'Olcum' is not assignable to type 'null'.
p4.ts(17,7): error TS2322: Type 'number' is not assignable to type 'null'.
```

Dağılım burada da iş görüyor: `Sonuc<Olcum>` iki üyeli bir birleşimdir; `BasariTipi`
hesabında başarı üyesi `Olcum`, hata üyesi `never` verir ve birleşim `Olcum`a indirgenir.

Bu kalıbın değeri, bir tipin **tanımını tekrarlamadan** parçasına erişebilmektir. `Sonuc`
tipinin yapısı değişirse `BasariTipi` da değişir; ikisi arasında elle tutulan bir bağ
kalmaz.

#### Koşullu Dönüş Tipinin Sınırı

Koşullu tipler fonksiyon imzalarında kullanılabilir, ancak gövde içinde çözülmezler:

```typescript
type Bicim<T> = T extends number ? string : number;

function bicimle<T extends number | boolean>(deger: T): Bicim<T> {
  if (typeof deger === "number") {
    return deger.toFixed(1);
  }
  return 0;
}

console.log(bicimle(21.4), bicimle(true));
```

```text
p7.ts(5,5): error TS2322: Type 'string' is not assignable to type 'Bicim<T>'.
p7.ts(7,3): error TS2322: Type 'number' is not assignable to type 'Bicim<T>'.
```

Gövde içinde `T` henüz bilinmediği için `Bicim<T>` çözülemez; derleyici, `typeof deger`
sınamasının `T`yi de daralttığını çıkaramaz. Tanı iki dalda birden gelir.

Bu, tip sisteminin bilinen bir sınırıdır. İki çözüm vardır: gövdede tek bir tip iddiası
kullanmak — sınırı görünür kılan, imzada verilen sözü gövdenin taşıdığını kabul eden bir
borç — ya da koşullu tip yerine aşırı yükleme yazmak. İkincisi, Fonksiyon Tipleri dersinde
kurulan yapıdır ve çağıranlar için aynı sonucu verir.

Genel ilke: koşullu tipler **tip hesaplamak** için güçlüdür, **gerçekleştirimi
yönlendirmek** için değil.

#### Özet

- Koşullu tip, atanabilirlik sınamasına göre iki tipten birini seçer; `extends` burada
  kalıtım değil atanabilirlik anlamındadır.
- Çıplak bir tip parametresi birleşimse koşul her üye için ayrı değerlendirilir ve
  sonuçların birleşimi alınır; `[T] extends [U]` yazımı bu dağılımı kapatır.
- Dağılım ve `never` birlikte, bir birleşimden üye eleyen süzgeçler kurar.
- `infer`, koşulun sol tarafında bir tip değişkeni bildirir ve eşleştirme sırasında ona
  karşılık gelen tipi çıkarır; bir tipin parçasına tanımı tekrarlamadan erişmeyi sağlar.
- Koşullu dönüş tipleri fonksiyon gövdesi içinde çözülmez; bu durumda tip iddiası ya da
  aşırı yükleme gerekir.

#### Sonraki Adım

Anahtar yeniden eşlemesi dersinde `` `oku${Capitalize<string & A>}` `` biçiminde bir yazım
kullanıldı ve alan adı hesaplandı. Dizgilerin tip düzeyinde birleştirilmesi ve
ayrıştırılması ayrı bir araç kümesidir. Sonraki ders şablon dizgi tiplerini ve bunların
`infer` ile birlikte dizgi biçimlerini çözümlemekte nasıl kullanıldığını ele alacak.

### Şablon Dizgi Tipleri

Source: https://academia.sh/tr/kurslar/typescript/sablon-dizgi-tipleri

Dizgi biçimlerinin tiple ifadesi, birleşimler üzerinde çapraz çarpım, yerleşik dizgi dönüşümleri, infer ile biçim ayrıştırma, açık uçlu kalıplar ve sabit tipin string içinde erimesi.

Eşlenmiş tipler dersinde `` `oku${Capitalize<string & A>}` `` yazımı geçti ve bir alan adı
hesaplandı. **Şablon dizgi tipi** (template literal type), dizgi sabit tiplerini
birleştirerek yeni sabit tipler üretir.

Yazımı JavaScript'in şablon dizgilerinin aynısıdır, ancak değerler değil tipler
birleştirilir. Bu ders, bu araçla dizgi biçimlerinin nasıl tiplendiğini, birleşimler
üzerindeki davranışını ve sınırlarını ele alır.

#### Biçim Tanımlama

Ölçüm kayıtları çoğu zaman bir kanal adıyla anılır: hangi sensörden, hangi birimde. Bu ad
bir biçim taşır ve biçim tiplenebilir:

```typescript
type Sensor = "sicaklik" | "basinc";
type Birim = "C" | "Pa";

type Kanal = `${Sensor}.${Birim}`;

const kanallar: Kanal[] = ["sicaklik.C", "sicaklik.Pa", "basinc.C", "basinc.Pa"];
console.log(kanallar.length, kanallar[0]);
```

Çıktı `4 sicaklik.C`tir. Aynı tanımlarla, biçime uymayan bir atama ayrı bir dosyada
(`q1.ts`) denenirse:

```typescript
// q1.ts — biçime uymayan kanal adı
type Sensor = "sicaklik" | "basinc";
type Birim = "C" | "Pa";

type Kanal = `${Sensor}.${Birim}`;

const yanlis: Kanal = "sicaklik.%";
```

```text
q1.ts(7,7): error TS2820: Type '"sicaklik.%"' is not assignable to type '"basinc.C" | "basinc.Pa" | "sicaklik.C" | "sicaklik.Pa"'. Did you mean '"sicaklik.C"'?
```

Tanı, hesabın sonucunu açıkça yazıyor: `Kanal` tipi dört sabit tipin birleşimidir. Yerine
koyulan her tip bir birleşimse, sonuç bu birleşimlerin **çapraz çarpımıdır** — iki sensör
ve iki birim dört kanal verir.

Tanının sonundaki öneri de derleyicinin sabit tip birleşimlerinde yaptığı bir
kolaylıktır: yazım hatası olma olasılığı yüksek olan değerler için en yakın geçerli
sabiti gösterir.

Bu tip, dizginin yalnızca bir dizgi olmadığını, belirli bir biçim taşıdığını kaydeder.
Yapısal Tip Uyumluluğu dersindeki markalı tiple aynı amaca hizmet eder, ama biçim
gerçekten tanımlanabildiği için tip iddiasına gerek kalmaz.

#### Yerleşik Dizgi Dönüşümleri

Derleyici dört dizgi dönüşümünü yerleşik olarak sağlar: `Uppercase`, `Lowercase`,
`Capitalize` ve `Uncapitalize`.

```typescript
type Sensor = "sicaklik" | "basinc";

type BuyukSensor = Uppercase<Sensor>;
type OlaySensorAdi = `olcum${Capitalize<Sensor>}Alindi`;

const buyuk: BuyukSensor = "SICAKLIK";
const olay: OlaySensorAdi = "olcumBasincAlindi";
console.log(buyuk, olay);
```

Çıktı `SICAKLIK olcumBasincAlindi`dir. Aynı tanımlarla, harf büyüklüğü yanlış bir olay
adı ayrı bir dosyada (`q3.ts`) denenirse:

```typescript
// q3.ts — harf büyüklüğü yanlış olay adı
type Sensor = "sicaklik" | "basinc";

type BuyukSensor = Uppercase<Sensor>;
type OlaySensorAdi = `olcum${Capitalize<Sensor>}Alindi`;

const yanlisOlay: OlaySensorAdi = "olcumbasincAlindi";
```

```text
q3.ts(7,7): error TS2820: Type '"olcumbasincAlindi"' is not assignable to type '"olcumBasincAlindi" | "olcumSicaklikAlindi"'. Did you mean '"olcumBasincAlindi"'?
```

Bu dönüşümler derleyicinin içine gömülüdür; tip düzeyinde yazılamazlar. Harf dönüşümleri
yerel ayardan bağımsız, sabit bir kurala göre yapılır — dolayısıyla Türkçedeki noktasız
`ı` ve noktalı `i` ayrımı gibi yerel kurallar burada geçerli değildir. Kanal ve olay
adlarında ASCII kalmanın bir nedeni de budur.

#### Anahtar Tablosu Kurmak

Şablon dizgi tipi bir eşlenmiş tipin anahtar kaynağı olabilir. Sonuç, biçimi tanımlı ve
tam bir tablodur:

```typescript
type Sensor = "sicaklik" | "basinc";
type Birim = "C" | "Pa";
type Kanal = `${Sensor}.${Birim}`;

type KanalTablosu = {
  [K in Kanal]: number;
};

const sonDegerler: KanalTablosu = {
  "sicaklik.C": 21.4,
  "sicaklik.Pa": 0,
  "basinc.C": 0,
  "basinc.Pa": 101325,
};

console.log(sonDegerler["basinc.Pa"], Object.keys(sonDegerler).length);
```

Çıktı `101325 4`tür.

Tablonun anahtarları elle sayılmamıştır; `Sensor` birleşimine üçüncü bir sensör
eklendiğinde tablo eksik kalır ve derleyici tanı verir. Ölçüm modelinin genişlemesi,
modeli kullanan veri yapılarının güncellenmesini zorunlu kılar.

#### `infer` ile Biçim Ayrıştırma

Şablon dizgi tipleri koşullu tiplerin sol tarafında da kullanılabilir. `infer` ile biçimin
parçaları çıkarılır:

```typescript
type SensorAdi<K> = K extends `${infer S}.${string}` ? S : never;
type BirimAdi<K> = K extends `${string}.${infer B}` ? B : never;

type Kanal = "sicaklik.C" | "basinc.Pa";

declare const s: SensorAdi<Kanal>;
declare const b: BirimAdi<Kanal>;

const x: null = s;
const y: null = b;
```

```text
q5.ts(9,7): error TS2322: Type '"basinc" | "sicaklik"' is not assignable to type 'null'.
  Type '"basinc"' is not assignable to type 'null'.
q5.ts(10,7): error TS2322: Type '"C" | "Pa"' is not assignable to type 'null'.
  Type '"C"' is not assignable to type 'null'.
```

Ayrıştırma dağılımla birlikte çalışmıştır: `Kanal` birleşiminin her üyesi ayrı
eşleştirilmiş, sonuçların birleşimi alınmıştır.

Bu tip, çalışma zamanındaki bir ayrıştırmanın imzasında kullanılabilir:

```typescript
type Sensor = "sicaklik" | "basinc";
type Birim = "C" | "Pa";
type Kanal = `${Sensor}.${Birim}`;

type SensorAdi<K extends Kanal> = K extends `${infer S}.${string}` ? S : never;

function sensorAdi<K extends Kanal>(kanal: K): SensorAdi<K> {
  return kanal.split(".")[0] as SensorAdi<K>;
}

const ad = sensorAdi("basinc.Pa");
console.log(ad);
```

Çıktı `basinc`tır. Dönüş tipini yanlış bir sabit tiple eşleştiren bir atama, aynı
tanımlarla ayrı bir dosyada (`q6.ts`) denenirse:

```typescript
// q6.ts — dönüş tipiyle uyuşmayan atama
type Sensor = "sicaklik" | "basinc";
type Birim = "C" | "Pa";
type Kanal = `${Sensor}.${Birim}`;

type SensorAdi<K extends Kanal> = K extends `${infer S}.${string}` ? S : never;

function sensorAdi<K extends Kanal>(kanal: K): SensorAdi<K> {
  return kanal.split(".")[0] as SensorAdi<K>;
}

const yanlisAtama: "sicaklik" = sensorAdi("basinc.Pa");
```

```text
q6.ts(12,7): error TS2322: Type '"basinc"' is not assignable to type '"sicaklik"'.
```

`sensorAdi("basinc.Pa")` çağrısının tipi `string` değil `"basinc"`tır. Çağrı yerinde
verilen sabit tipten, dönüş tipi hesaplanmıştır.

Gövdedeki `as SensorAdi<K>` yazımına dikkat: önceki derste kurulan sınır burada da
geçerlidir, koşullu dönüş tipi gövde içinde çözülmez. Bu, imzada verilen sözü gövdenin
taşıdığını kabul eden bir borçtur ve tek satırda toplandığı için sınanabilir durumdadır.

#### Açık Uçlu Kalıplar

Yerine koyulan tipin bir birleşim olması zorunlu değildir; `string` de yazılabilir. Sonuç,
sabit bir küme değil bir **biçim kalıbıdır**:

```typescript
type OlcumOlayi = `olcum:${string}`;
type UyariOlayi = `uyari:${string}`;
type Olay = OlcumOlayi | UyariOlayi;

function yonlendir(olay: Olay): string {
  if (olay.startsWith("olcum:")) {
    return `kayda alindi: ${olay}`;
  }
  return `bildirildi: ${olay}`;
}

console.log(yonlendir("olcum:alindi"));
console.log(yonlendir("uyari:esik-asildi"));
```

Çıktı:

```text
kayda alindi: olcum:alindi
bildirildi: uyari:esik-asildi
```

`` `olcum:${string}` `` tipi, `"olcum:"` ile başlayan **her** dizgiyi kapsar. Kapalı bir
küme olmadığı için önceki bölümdeki gibi tek tek sayılamaz; buna karşılık ön ek zorunluluğu
korunur. Dosyanın sonuna, bir boş satırdan sonra `const yanlis: OlcumOlayi = "uyari:alindi";`
satırı eklenirse:

```text
u12.ts(15,7): error TS2322: Type '"uyari:alindi"' is not assignable to type '`olcum:${string}`'.
```

Bu kalıp, olay adları veya anahtar önekleri gibi gövdesi serbest ama biçimi bağlayıcı
dizgilerde kullanılır. Sabit bir liste tutmak gerekmez; ön ek yanlış yazıldığında derleyici
bildirir.

#### Sabit Tipin `string` İçinde Erimesi

Açık uçlu kalıplarla birlikte gelen bir tuzak vardır. Bir sabit tip, `string` ile aynı
birleşime konduğunda **kaybolur**:

```typescript
type Kanal = "sicaklik.C" | string;

declare const k: Kanal;
const x: null = k;
```

```text
u8.ts(4,7): error TS2322: Type 'string' is not assignable to type 'null'.
```

Tanı `Kanal` yerine `string` yazıyor: `"sicaklik.C"` üyesi zaten `string` kümesinin içinde
olduğu için birleşim `string`e indirgenmiştir. Sonuç, hiçbir kısıt taşımayan bir tiptir —
oysa yazılış, sanki bir öneri listesi sunuyormuş izlenimi verir.

Bu, "bilinen değerler ve serbest metin" modellemek istendiğinde sık düşülen bir hatadır.
Doğru araç, kısıtın gerçekten istendiği yerde kapalı bir birleşim ya da açık uçlu bir
kalıp kullanmaktır:

```typescript
type Kanal = `${"sicaklik" | "basinc"}.${"C" | "Pa"}`;

function birimAyristir(kanal: Kanal): string {
  return kanal.split(".")[1];
}

const kanallar: Kanal[] = ["sicaklik.C", "basinc.Pa"];
console.log(kanallar.map(birimAyristir).join(","));
```

Çıktı `C,Pa`dır. Aynı tanımların ardından serbest bir dizgi bu işleve verilirse
(ayrı dosya: `u13.ts`):

```typescript
// u13.ts — serbest dizgiyi kapalı kümeye vermek
type Kanal = `${"sicaklik" | "basinc"}.${"C" | "Pa"}`;

function birimAyristir(kanal: Kanal): string {
  return kanal.split(".")[1];
}

const serbest: string = "sicaklik.C";
birimAyristir(serbest);
```

```text
u13.ts(9,15): error TS2345: Argument of type 'string' is not assignable to parameter of type '"basinc.C" | "basinc.Pa" | "sicaklik.C" | "sicaklik.Pa"'.
```

Sabit tiplerin kaybolmadığı görülüyor: tip hâlâ dört değerli kapalı bir kümedir ve serbest
bir dizgi kabul edilmez. Sabit tip birleşimine `string` eklemek, tam da bu güvenceyi
ortadan kaldıran işlemdir.

#### Birleşim Büyüklüğü Sınırı

Çapraz çarpım hızlı büyür. İki üç üyeli birleşim dokuz sonuç verir; her ek bileşen çarpanı
artırır. Derleyicinin bir üst sınırı vardır:

```typescript
type Harf = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j";
type Kombinasyon = `${Harf}${Harf}${Harf}${Harf}${Harf}`;
declare const x: Kombinasyon;
const y: null = x;
```

```text
q9.ts(2,20): error TS2590: Expression produces a union type that is too complex to represent.
```

On harfin beşli bileşimi yüz bin sabit tip demektir ve derleyici bunu reddeder. Sınır
sürüme göre değişebilir; asıl ders, biçimin **kapalı** olduğu durumlarda şablon dizgi
tipinin uygun, açık olduğu durumlarda uygunsuz olmasıdır.

Algoritmalar kursundaki kombinatoryel patlama kavramı burada tip denetleyicisinin
kendisinde ortaya çıkar. Serbest biçimli dizgiler için doğru araç, sabit tip birleşimi
değil çalışma zamanı doğrulaması ve markalı tiptir.

#### Özet

- Şablon dizgi tipi, sabit tipleri birleştirerek dizgi biçimlerini tip düzeyinde tanımlar;
  yerine koyulan tipler birleşimse sonuç, birleşimlerin çapraz çarpımıdır.
- `Uppercase`, `Lowercase`, `Capitalize` ve `Uncapitalize` derleyiciye gömülüdür ve yerel
  ayardan bağımsız çalışır.
- Şablon dizgi tipi bir eşlenmiş tipin anahtar kaynağı olarak kullanıldığında, biçimi
  tanımlı ve tam bir tablo elde edilir; koşullu tipin sol tarafında `infer` ile biçim
  parçaları çıkarılır ve sonuç birleşimler üzerinde dağılır.
- Yerine `string` konduğunda tip kapalı bir küme değil açık uçlu bir biçim kalıbı olur;
  ön ek zorunluluğu korunur, değerler sayılamaz.
- Bir sabit tip `string` ile aynı birleşime konduğunda erir ve birleşim `string`e
  indirgenir; "bilinen değerler ve serbest metin" bu yolla modellenemez.
- Çapraz çarpım hızla büyür ve derleyici belirli bir büyüklükten sonra `TS2590` tanısı
  verir; kapalı sabit tip kümeleri yalnızca sınırlı büyüklükte kurulabilir.

#### Sonraki Adım

Bu konuda elle yazılan dönüşümlerin çoğu — salt okunur sürüm, seçmeli sürüm, birleşimden
üye eleme — standart kitaplıkta hazır tiplerle bulunur. Sonraki ders bu yardımcı tipleri
tanıtacak, hangilerinin hangi mekanizmayla kurulduğunu gösterecek ve ölçüm modelinin
türevlerini bunlarla yeniden yazacak.

### Yardımcı Tipler

Source: https://academia.sh/tr/kurslar/typescript/yardimci-tipler

Standart kitaplığın hazır tip dönüşümleri; alan değiştiricileri, alan seçme ve dışlama, tablo kurma, birleşim süzme ve imzalardan tip çıkarma araçları.

Bu konuda elle yazılan dönüşümlerin çoğu — salt okunur sürüm, seçmeli sürüm, birleşimden
üye eleme — standart kitaplıkta hazır bulunur. Bunlara **yardımcı tip** (utility type)
denir.

Hepsi önceki derslerde kurulan mekanizmalarla yazılmıştır: eşlenmiş tipler, koşullu
tipler, `keyof` ve `infer`. Bu derste her birinin ne yaptığı, nasıl kurulduğu ve ölçüm
modelinin türevlerini yazmakta nasıl kullanıldığı ele alınır.

#### Alan Değiştiricileri ve Alan Seçimi

En sık kullanılan dört dönüşüm, ölçüm modelinin dört türevini tek satırda üretir:

```typescript
interface Olcum {
  kimlik: string;
  sensor: string;
  deger: number;
  birim: "C" | "Pa" | "%";
  zaman: number;
}

type OlcumOzeti = Pick<Olcum, "kimlik" | "deger" | "birim">;
type OlcumGirdisi = Omit<Olcum, "kimlik" | "zaman">;
type OlcumYamasi = Partial<Omit<Olcum, "kimlik">>;
type ArsivKaydi = Readonly<Olcum>;

const ozet: OlcumOzeti = { kimlik: "s-01", deger: 21.4, birim: "C" };
const girdi: OlcumGirdisi = { sensor: "sicaklik", deger: 21.4, birim: "C" };
const yama: OlcumYamasi = { deger: 22.1 };
const arsiv: ArsivKaydi = {
  kimlik: "s-01",
  sensor: "sicaklik",
  deger: 21.4,
  birim: "C",
  zaman: 1706000000000,
};

console.log(ozet.kimlik, girdi.sensor, yama.deger, arsiv.zaman);
```

Çıktı `s-01 sicaklik 22.1 1706000000000`dir.

Dört tipin anlamı şudur. `OlcumOzeti`, listeleme ekranının ihtiyaç duyduğu üç alanı taşır.
`OlcumGirdisi`, yeni kayıt oluştururken verilen alanlardır — kimlik ve zaman damgası
sistem tarafından üretilir. `OlcumYamasi`, kısmi güncelleme isteğidir; kimlik hariç her
alan verilebilir ve hiçbiri zorunlu değildir. `ArsivKaydi` ise değiştirilemeyen bir
kayıttır.

Dördü de asıl tipten hesaplandığı için `Olcum` tipine yeni bir alan eklendiğinde uygun
türevler kendiliğinden büyür. Elle yazılsalardı bu bağ kopardı.

Kısıtların gerçekten uygulandığı iki tanıyla görülür:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
  zaman: number;
}

type OlcumGirdisi = Omit<Olcum, "kimlik">;
type ArsivKaydi = Readonly<Olcum>;

const girdi: OlcumGirdisi = { kimlik: "s-01", deger: 21.4, zaman: 1 };

declare const arsiv: ArsivKaydi;
arsiv.deger = 22.1;
```

```text
r2.ts(10,31): error TS2353: Object literal may only specify known properties, and 'kimlik' does not exist in type 'OlcumGirdisi'.
r2.ts(13,7): error TS2540: Cannot assign to 'deger' because it is a read-only property.
```

Bu yardımcı tiplerin tanımları, eşlenmiş tipler dersinde elle yazılanların aynısıdır:

| Yardımcı tip | Karşılığı |
|---|---|
| `Partial<T>` | `{ [A in keyof T]?: T[A] }` |
| `Required<T>` | `{ [A in keyof T]-?: T[A] }` |
| `Readonly<T>` | `{ readonly [A in keyof T]: T[A] }` |
| `Pick<T, A>` | `{ [B in A]: T[B] }` |
| `Record<A, D>` | `{ [B in A]: D }` |

`Omit` bu listede yoktur, çünkü doğrudan eşlemeyle değil bir bileşimle kurulur:
`Pick<T, Exclude<keyof T, A>>`. Önce dışlanacak adlar `keyof T` birleşiminden çıkarılır,
kalan adlar seçilir.

#### `Omit` ile `Pick` Arasındaki Denetim Farkı

Bu bileşimin gözden kaçan bir sonucu vardır:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

type A = Omit<Olcum, "olmayanAlan">;
type B = Pick<Olcum, "olmayanAlan">;
```

```text
r3.ts(7,22): error TS2344: Type '"olmayanAlan"' does not satisfy the constraint 'keyof Olcum'.
```

Tanı yalnızca `Pick` için gelir. `Omit` tipinin ikinci parametresinin kısıtı `keyof T`
değil, herhangi bir özellik anahtarıdır; var olmayan bir alan adı sessizce yok sayılır ve
sonuç `Olcum` ile aynı olur.

Bunun bakım açısından bedeli şudur: `Olcum` tipindeki bir alan yeniden adlandırıldığında,
onu dışlayan `Omit` yazımları sessizce etkisiz hâle gelir ve dışlanması gereken alan geri
gelir. `Pick` yazımlarında ise derleyici tanı verir.

Pratik sonuç: alan kümesi küçükse `Pick` yeğlenir. `Omit` gerekiyorsa, kısıtı sıkılaştıran
bir sarmalayıcı yazılabilir:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

type Disla<T, A extends keyof T> = Omit<T, A>;

type Guvenli = Disla<Olcum, "kimlik">;
const g: Guvenli = { deger: 21.4 };
console.log(g.deger);
```

Çıktı `21.4`tür. Aynı sarmalayıcıyla var olmayan bir alanı dışlamaya çalışan satır ayrı
bir dosyada (`r7.ts`) denenirse:

```typescript
// r7.ts — var olmayan alanı dışlamak
interface Olcum {
  kimlik: string;
  deger: number;
}

type Disla<T, A extends keyof T> = Omit<T, A>;

type Yanlis = Disla<Olcum, "olmayanAlan">;
```

```text
r7.ts(9,28): error TS2344: Type '"olmayanAlan"' does not satisfy the constraint 'keyof Olcum'.
```

Tek fark, tip parametresine `extends keyof T` kısıtının konmasıdır; gövde yine `Omit`
kullanır.

#### Birleşim Süzgeçleri

Üç yardımcı tip, koşullu tiplerin dağılım davranışı üzerine kurulur:

```typescript
type Birim = "C" | "Pa" | "%";
type Aralik = [alt: number, ust: number];

type Araliklar = Record<Birim, Aralik>;
type SayisalBirim = Exclude<Birim, "%">;
type YuzdeBirim = Extract<Birim, "%" | "ppm">;
type TanimliDeger = NonNullable<number | null | undefined>;

const araliklar: Araliklar = {
  C: [-40, 85],
  Pa: [0, 200000],
  "%": [0, 100],
};
const sayisal: SayisalBirim = "Pa";
const yuzde: YuzdeBirim = "%";
const deger: TanimliDeger = 21.4;

console.log(araliklar.C[1], sayisal, yuzde, deger);
```

Çıktı `85 Pa % 21.4`tür.

Tanımları koşullu tiplerle tek satırdır:

- `Exclude<T, U>` — `T extends U ? never : T`
- `Extract<T, U>` — `T extends U ? T : never`
- `NonNullable<T>` — `T & {}`

`Extract` örneğinde `"ppm"` değeri `Birim` birleşiminde bulunmaz ve sonuca girmez;
kesişim alınıyor gibi düşünülebilir. `NonNullable` ise boş nesne tipiyle kesişim alarak
`null` ve `undefined` üyelerini eler.

#### İmzalardan Tip Çıkarma

Son grup, `infer` üzerine kurulur ve var olan bir fonksiyonun imzasından tip türetir:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

function olcumOlustur(kimlik: string, deger: number) {
  return { kimlik, deger, olusturmaZamani: 0 };
}

async function olcumGetir(kimlik: string): Promise<Olcum> {
  return { kimlik, deger: 21.4 };
}

type OlusanKayit = ReturnType<typeof olcumOlustur>;
type OlusturmaArgumanlari = Parameters<typeof olcumOlustur>;
type GetirilenKayit = Awaited<ReturnType<typeof olcumGetir>>;

const kayit: OlusanKayit = { kimlik: "s-01", deger: 21.4, olusturmaZamani: 0 };
const argumanlar: OlusturmaArgumanlari = ["s-02", 22.1];
const getirilen: GetirilenKayit = { kimlik: "s-03", deger: 23.0 };

console.log(kayit.olusturmaZamani, olcumOlustur(...argumanlar).kimlik, getirilen.deger);
```

Çıktı `0 s-02 23`tür. Eksik argümanlı bir atama, aynı `olcumOlustur` tanımıyla ayrı bir
dosyada (`r5.ts`) denenirse:

```typescript
// r5.ts — eksik argümanlı demet
function olcumOlustur(kimlik: string, deger: number) {
  return { kimlik, deger, olusturmaZamani: 0 };
}

type OlusturmaArgumanlari = Parameters<typeof olcumOlustur>;

const yanlis: OlusturmaArgumanlari = ["s-04"];
```

```text
r5.ts(8,7): error TS2322: Type '[string]' is not assignable to type '[kimlik: string, deger: number]'.
  Source has 1 element(s) but target requires 2.
```

Tanı, `Parameters` sonucunun adlandırılmış bir demet olduğunu gösteriyor. `ReturnType`
`olcumOlustur` fonksiyonunun çıkarılmış dönüş tipini verir; `Awaited`, Eşzamansız
JavaScript kursunda tanıtılan sözün çözüldüğünde ürettiği değerin tipini alır ve iç içe
sözleri de açar.

Bu grubun tipik kullanım yeri, bir fonksiyonun dönüş tipini elle yazmadan başka bir yerde
kullanmaktır. Bir uyarı gerekir: bu bağ tek yönlüdür ve fonksiyonun gövdesi değiştiğinde
türetilmiş tip de sessizce değişir. Tip Çıkarımı dersinde kurulan kural burada geçerlidir
— dışa açılan bir sınırın tipi, çıkarıma bırakılmak yerine yazılmalıdır.

#### Yardımcı Tip Seçimi

| İhtiyaç | Yardımcı tip |
|---|---|
| Tüm alanları seçmeli yap | `Partial<T>` |
| Tüm alanları zorunlu yap | `Required<T>` |
| Tüm alanları salt okunur yap | `Readonly<T>` |
| Belirli alanları tut | `Pick<T, A>` |
| Belirli alanları çıkar | `Omit<T, A>` |
| Anahtar kümesinden tablo kur | `Record<A, D>` |
| Birleşimden üye çıkar | `Exclude<T, U>` |
| Birleşimden üye seç | `Extract<T, U>` |
| Boş değerleri ele | `NonNullable<T>` |
| İmzadan tip türet | `ReturnType`, `Parameters`, `Awaited` |

Tablodaki her satır bu konuda kurulan bir mekanizmaya dayanır. Yardımcı tipleri ezberlemek
gerekmez; ne yaptıkları, nasıl kuruldukları bilindiğinde kendiliğinden hatırlanır ve
listede olmayan bir dönüşüm gerektiğinde yenisi yazılabilir.

#### Özet

- Yardımcı tipler, önceki derslerde kurulan eşlenmiş ve koşullu tip mekanizmalarının
  standart kitaplıktaki hazır biçimleridir.
- `Pick`, `Omit`, `Partial` ve `Readonly` bir veri modelinin türevlerini asıl tipten
  hesaplar; model büyüdüğünde türevler birlikte büyür.
- `Omit` ikinci parametresini `keyof T` ile kısıtlamaz; var olmayan bir alan adı sessizce
  yok sayılır, `Pick` ise `TS2344` tanısı verir.
- `Exclude`, `Extract` ve `NonNullable`, koşullu tiplerin dağılım davranışı üzerine
  kurulmuş birleşim süzgeçleridir.
- `ReturnType`, `Parameters` ve `Awaited`, `infer` kullanarak var olan imzalardan tip
  türetir; türetilen tip gövde değiştiğinde sessizce değişir.

#### Sonraki Adım

Ölçüm kayıtları her zaman düz bir yapı taşımaz: bir kaydın altında alt kayıtlar, bir
yapılandırma nesnesinin altında iç içe bölümler bulunabilir. Böyle yapıların derinliğine
uygulanan dönüşümler — her düzeyde salt okunur yapma, iç içe anahtar yollarını sayma —
tipin kendisine başvurmasını gerektirir. Sonraki ders özyinelemeli tipleri ve derleyicinin
onlara koyduğu sınırları ele alacak.

### Özyinelemeli Tipler

Source: https://academia.sh/tr/kurslar/typescript/ozyinelemeli-tipler

Kendine başvuran tip tanımları, derin dönüşümler, tip düzeyinde özyineleme ile anahtar yolu üretimi ve derleyicinin açılım derinliği sınırı.

Ölçüm kayıtları her zaman düz bir yapı taşımaz: bir kaydın altında alt kayıtlar, bir
yapılandırma nesnesinin altında iç içe bölümler bulunur. Böyle yapıların tipi, kendisine
başvurmak zorundadır.

Tip Takma Adları dersinde bu olanağın sınırı belirlenmişti: nesne alanı içindeki başvuru
geçerli, doğrudan başvuru geçersizdi. Bu ders, o olanağın ne kadar ileri götürülebileceğini
— derin dönüşümler ve tip düzeyinde özyineleme — ve derleyicinin nerede durduğunu ele
alır.

#### Kendine Başvuran Veri Tipleri

Yapısı önceden bilinmeyen, iç içe geçebilen veri için tipik örnek JSON'un kendisidir:

```typescript
type JsonDegeri =
  | string
  | number
  | boolean
  | null
  | JsonDegeri[]
  | { [anahtar: string]: JsonDegeri };

function derinlik(deger: JsonDegeri): number {
  if (Array.isArray(deger)) {
    return 1 + Math.max(0, ...deger.map(derinlik));
  }
  if (typeof deger === "object" && deger !== null) {
    return 1 + Math.max(0, ...Object.values(deger).map(derinlik));
  }
  return 0;
}

const kayit: JsonDegeri = {
  kimlik: "s-01",
  deger: 21.4,
  etiketler: ["kazan", "vardiya-3"],
  konum: { bina: "A", kat: 2 },
};

console.log(derinlik(kayit));
console.log(derinlik(21.4));
```

Çıktı:

```text
2
0
```

Tip, altı üyeli bir birleşimdir ve son iki üyesi kendisine başvurur. Başvurular birer
alan tipi içinde olduğu için derleyici onları yalnızca gerektiğinde çözer; sonsuz açılım
oluşmaz.

Tipin bir güvencesi de vardır. Dosyanın sonuna, bir boş satırdan sonra
`const gecersiz: JsonDegeri = { zaman: new Date() };` satırı eklenirse:

```text
s1.ts(29,7): error TS2322: Type '{ zaman: Date; }' is not assignable to type 'JsonDegeri'.
  Types of property 'zaman' are incompatible.
    Type 'Date' is not assignable to type 'JsonDegeri | undefined'.
      Type 'Date' is not assignable to type '{ [anahtar: string]: JsonDegeri; }'.
        Index signature for type 'string' is missing in type 'Date'.
```

Bu, dizileştirilebilir veriyi dizileştirilemeyenden ayıran bir sınamadır. `JSON.stringify`
çağrısı bir tarih nesnesini sessizce dizgiye çevirir ve ayrıştırma sonrası tip değişir;
`JsonDegeri` tipi bu sessiz dönüşümü derleme anına taşır.

`derinlik` fonksiyonu da özyinelemelidir ve tipin yapısını izler: Programlama Temelleri
kursundaki özyineleme dersinde kurulan taban durumu ile indirgeme adımı, burada tipin
üyelerine karşılık gelir.

#### Derin Dönüşümler

Yardımcı tipler dersindeki `Readonly` yalnızca birinci düzeye uygulanır:

```typescript
interface Yapilandirma {
  ad: string;
  esikler: { alt: number; ust: number };
}

const sig: Readonly<Yapilandirma> = {
  ad: "kazan-2",
  esikler: { alt: -40, ust: 85 },
};

sig.esikler.ust = 90;
console.log(sig.esikler.ust);
```

Bu dosya tip denetiminden **geçer** ve `90` yazar. `sig.esikler` alanı salt okunurdur,
ancak işaret ettiği nesnenin alanları değildir.

Özyinelemeli bir eşlenmiş tip bu boşluğu kapatır:

```typescript
type DerinSaltOkunur<T> = {
  readonly [A in keyof T]: T[A] extends object ? DerinSaltOkunur<T[A]> : T[A];
};

interface Yapilandirma {
  ad: string;
  esikler: { alt: number; ust: number };
  kanallar: { sensor: string; birim: string }[];
}

const yapilandirma: DerinSaltOkunur<Yapilandirma> = {
  ad: "kazan-2",
  esikler: { alt: -40, ust: 85 },
  kanallar: [{ sensor: "sicaklik", birim: "C" }],
};

console.log(yapilandirma.esikler.ust, yapilandirma.kanallar[0].sensor);
```

Çıktı `85 sicaklik`tır. Aynı tanımlarla, iç içe alanı değiştirmeye çalışan satır ayrı bir
dosyada (`s3.ts`) denenirse:

```typescript
// s3.ts — derin salt okunur alana atama
type DerinSaltOkunur<T> = {
  readonly [A in keyof T]: T[A] extends object ? DerinSaltOkunur<T[A]> : T[A];
};

interface Yapilandirma {
  ad: string;
  esikler: { alt: number; ust: number };
  kanallar: { sensor: string; birim: string }[];
}

const yapilandirma: DerinSaltOkunur<Yapilandirma> = {
  ad: "kazan-2",
  esikler: { alt: -40, ust: 85 },
  kanallar: [{ sensor: "sicaklik", birim: "C" }],
};

yapilandirma.esikler.ust = 90;
```

```text
s3.ts(18,22): error TS2540: Cannot assign to 'ust' because it is a read-only property.
```

Eşleme her alanda `T[A] extends object` koşulunu sınar; nesne olan alanlarda kendini
yeniden uygular, ilkel alanlarda durur. Diziler de nesne olduğu için `kanallar` alanı
`readonly` bir diziye dönüşür ve elemanlarının alanları da salt okunur olur.

Önceki derslerdeki uyarı yine geçerlidir: bu güvence derleme anına aittir. Yapılandırma
nesnesi çalışma zamanında değiştirilebilir; derinlemesine dondurmak ayrı bir işlemdir.

#### Tip Düzeyinde Özyineleme

Özyineleme yalnızca veri biçimi tanımlamakla sınırlı değildir; tip düzeyinde hesap yapmak
için de kullanılır. İç içe bir nesnenin tüm anahtar yollarını üreten tip, bunun tipik
örneğidir:

```typescript
type Yol<T> = T extends object
  ? {
      [A in keyof T & string]: T[A] extends object ? A | `${A}.${Yol<T[A]>}` : A;
    }[keyof T & string]
  : never;

interface Yapilandirma {
  ad: string;
  esikler: { alt: number; ust: number };
  ag: { adres: string; kimlik: { kullanici: string } };
}

const gecerli: Yol<Yapilandirma>[] = [
  "ad",
  "esikler",
  "esikler.alt",
  "ag.kimlik.kullanici",
];
console.log(gecerli.length);
```

Çıktı `4`tür. Aynı tanımlarla, kümede olmayan bir yol ayrı bir dosyada (`s6.ts`)
denenirse:

```typescript
// s6.ts — kümede olmayan yol
type Yol<T> = T extends object
  ? {
      [A in keyof T & string]: T[A] extends object ? A | `${A}.${Yol<T[A]>}` : A;
    }[keyof T & string]
  : never;

interface Yapilandirma {
  ad: string;
  esikler: { alt: number; ust: number };
  ag: { adres: string; kimlik: { kullanici: string } };
}

const yanlisYol: Yol<Yapilandirma> = "esikler.orta";
```

```text
s6.ts(14,7): error TS2322: Type '"esikler.orta"' is not assignable to type '"ad" | "ag" | "ag.adres" | "ag.kimlik" | "ag.kimlik.kullanici" | "esikler" | "esikler.alt" | "esikler.ust"'.
```

Tanı, hesabın tam sonucunu yazıyor: sekiz geçerli yol. Yapı üç dersin bileşimidir. Eşlenmiş
tip her alan için bir değer üretir; `[keyof T & string]` indeksli erişimi bu değerlerin
birleşimini alır; şablon dizgi tipi alt yolları öne eklenmiş ad ile birleştirir; koşullu
tip nesne olmayan alanlarda özyinelemeyi durdurur.

Bu tipin kullanım yeri, yapılandırma anahtarını dizgi olarak alan bir arayüzdür. Yol
dizgisi artık serbest bir dizgi değil, kapalı bir kümedir; yanlış yazılmış bir anahtar
derleme anında yakalanır.

#### Derinlik Sınırı

Derleyici sonsuz açılımı önlemek için bir üst sınır uygular. Sınır, uzunluğu sayan bir
demet kurarak gözlenebilir:

```typescript
type Yinele<N extends number, B extends unknown[] = []> = B["length"] extends N
  ? B
  : Yinele<N, [...B, unknown]>;

type Sonuc = Yinele<1001>;
declare const s: Sonuc;
console.log(s.length);
```

```text
s10.ts(5,14): error TS2589: Type instantiation is excessively deep and possibly infinite.
```

Aynı dosyada `Yinele<999>` yazıldığında tanı gelmez. Sınırın tam değeri derleyici sürümüne
bağlıdır ve buna dayanan kod kırılgandır; önemli olan **bir sınırın bulunmasıdır**.

Sınırın altında kalan özyinelemelerin de bir bedeli vardır: her açılım derleyicinin
çalışma süresini artırır. Yol üretimi gibi tipler, geniş yapılandırma nesnelerinde tip
denetimini gözle görülür biçimde yavaşlatabilir. Algoritmalar kursundaki zaman ve alan
ödünleşimi burada derleme süresi ile tip kesinliği arasında kurulur.

Sınırsız derinlik gerektiren durumlar için tip düzeyi çözüm uygun değildir; doğru araç,
çalışma zamanı doğrulaması ve markalı tiptir.

#### Özyineleme Yazarken

Üç kural, özyinelemeli tiplerin sağlıklı kalmasını sağlar.

**Taban durumu açık yazılır.** Yukarıdaki her örnekte özyinelemenin durduğu bir koşul
vardır: `JsonDegeri` için ilkel üyeler, `DerinSaltOkunur` için `extends object` sınaması,
`Yol` için aynı sınama. Taban durumu olmayan bir tip, derleyicinin sınırına kadar açılır ve
`TS2589` verir.

**Derinlik veriden gelmemelidir.** Açılım derinliği tipin yapısına bağlı olmalı, çağıranın
verdiği bir sayıya değil. `Yinele<N>` örneği bilinçli olarak bu kuralı çiğneyerek sınırı
gösterir.

**Sonuç sınanır.** Tip düzeyinde hesaplanan bir tipin doğruluğu, beklenen değerleri içeren
bir dizi bildirerek sınanabilir — yukarıdaki `gecerli` bildirimi bunu yapar. Yanlış
hesaplanan bir tip, kullanıldığı her yerde şaşırtıcı tanılar üretir.

#### Özet

- Nesne alanı içindeki kendine başvuru geçerlidir; derleyici bu başvuruları yalnızca
  gerektiğinde çözer.
- `Readonly` gibi yardımcı tipler yalnızca birinci düzeye uygulanır; derin dönüşüm için
  özyinelemeli eşlenmiş tip yazılır.
- Eşlenmiş tip, indeksli erişim, şablon dizgi tipi ve koşullu tip birlikte, iç içe
  anahtar yollarını kapalı bir birleşim olarak üretebilir.
- Derleyici belirli bir açılım derinliğinden sonra `TS2589` tanısı verir; sınırın değeri
  sürüme bağlıdır ve ona dayanan kod kırılgandır.
- Özyinelemeli tipte taban durumu açıkça yazılır, derinlik veriden değil yapıdan gelir ve
  sonuç ayrı bir bildirimle sınanır.

#### Sonraki Adım

Buraya kadar tüm örnekler tek dosyalık ve elle verilmiş derleyici seçenekleriyle çalıştı.
Gerçek bir projede seçenekler bir yapılandırma dosyasında toplanır ve katılık düzeyi
bilinçli seçilir. Sonraki konu proje yapılandırmasını ele alacak: hangi bayrağın hangi hata
sınıfını yakaladığı, bildirim dosyalarının rolü, JavaScript ile birlikte çalışma ve tip
denetimi ile yayın çıktısının ayrılması.

## Proje Yapılandırması

Derleyici yapılandırması ve katılık bayrakları, bildirim dosyaları, JavaScript ile birlikte çalışma, modül çözümleme ve derleme akışı.

### Derleyici Yapılandırması

Source: https://academia.sh/tr/kurslar/typescript/derleyici-yapilandirmasi

Yapılandırma dosyasının yapısı, katılık şemsiyesi ve alt bayrakları, şemsiye dışındaki ek katılık seçenekleri ve hedef ile kitaplık ayarları.

Buraya kadar tüm örnekler tek dosyalık ve elle verilmiş seçeneklerle çalıştı. Gerçek bir
projede seçenekler bir yapılandırma dosyasında toplanır.

Bu dosya, derleyicinin hangi dosyaları okuyacağını, ne kadar katı davranacağını ve ne
üreteceğini belirler. Katılık düzeyi bir tercih değil, **hangi hata sınıflarının
yakalanacağı** kararıdır; bu ders o kararı örneklerle kurar.

#### Yapılandırma Dosyasının Yapısı

Yapılandırma dosyası `tsconfig.json` adıyla proje kökünde durur:

```json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitOverride": true,
    "noUnusedLocals": true,
    "noEmit": true
  },
  "include": ["src"]
}
```

İki bölüm vardır. `compilerOptions` derleyicinin davranışını, `include` ise hangi
dosyaların projeye ait olduğunu belirler. `exclude` ile alt kümeler dışlanabilir; `extends`
ile başka bir yapılandırma dosyası temel alınabilir.

Derleyicinin gerçekte hangi ayarlarla çalıştığı sorulabilir. Proje dizininde
`tsc --showConfig` komutu, varsayılanlarla tamamlanmış yapılandırmayı yazar:

```json
{
    "compilerOptions": {
        "target": "es2022",
        "module": "nodenext",
        "moduleResolution": "nodenext",
        "strict": true,
        "noUncheckedIndexedAccess": true,
        "exactOptionalPropertyTypes": true,
        "noImplicitOverride": true,
        "noUnusedLocals": true,
        "noEmit": true,
        "moduleDetection": "force"
    },
    "files": [
        "./src/olcum.ts"
    ],
    "include": [
        "src"
    ]
}
```

Çıktıda iki şey görülür. `files` listesi, `include` deseninin çözülmüş hâlidir — hangi
dosyaların gerçekten denetlendiği burada doğrulanır. `moduleDetection` seçeneği ise
yazılmadığı hâlde eklenmiştir; derleyicinin doldurduğu varsayılanlardır ve sürüme göre
değişebilir. Bir davranışın nereden geldiği araştırılırken ilk bakılacak yer bu çıktıdır.

#### Katılık Şemsiyesi

`strict` tek bir seçenek değil, bir grup seçeneği birlikte açan **şemsiyedir**. Etkisi,
aynı dosyayı iki ayarla derleyerek görülür:

```typescript
interface Olcum {
  kimlik: string;
  deger: number;
}

function ortalama(kayitlar) {
  return kayitlar.length;
}

const kayit: Olcum = null;
console.log(ortalama([kayit]), kayit.deger);
```

`"strict": false` ile derlendiğinde derleyici **hiçbir tanı üretmez**. `"strict": true` ile:

```text
src/gevsek.ts(6,19): error TS7006: Parameter 'kayitlar' implicitly has an 'any' type.
src/gevsek.ts(10,7): error TS2322: Type 'null' is not assignable to type 'Olcum'.
```

Aynı kaynak, aynı derleyici, iki farklı sonuç. Katılık kapalıyken tip katmanı yalnızca
yazılmış tipleri sınar; `null` her tipe girer, tipsiz parametreler denetlenmez. Bu, ilk
derste sözü edilen "kademeli eklenebilirlik" tasarımının bir sonucudur ve var olan bir
JavaScript kod tabanına geçişte anlamlıdır. Yeni bir projede kapalı bırakılması, tip
katmanının verdiği güvencenin büyük bölümünden vazgeçmek demektir.

Şemsiyenin altındaki başlıca seçenekler ve yakaladıkları:

| Seçenek | Yakaladığı |
|---|---|
| `noImplicitAny` | Tipi yazılmamış ve çıkarılamayan parametreler |
| `strictNullChecks` | `null` ve `undefined` değerlerin sessizce kullanılması |
| `strictFunctionTypes` | Fonksiyon parametrelerinde yön kuralının çiğnenmesi |
| `strictBindCallApply` | `call`, `apply` ve `bind` çağrılarında yanlış argümanlar |
| `strictPropertyInitialization` | Yapıcıda değer verilmeyen sınıf alanları |
| `strictBuiltinIteratorReturn` | Yerleşik yineleyicilerin dönüş tipinin `any` sayılması |
| `noImplicitThis` | Tipi belirsiz `this` bağlamı |
| `useUnknownInCatchVariables` | Yakalanan hatanın tipinin varsayılması |
| `alwaysStrict` | Çıktının katı kipte üretilmemesi |

Bu listenin sürümlerle büyüyebileceği unutulmamalıdır: `strict` açıkken derleyici
yükseltmesi yeni tanılar getirebilir. Bu, istenen bir davranıştır — yeni denetimler, daha
önce yakalanamayan hataları görünür kılar.

Son iki satırın etkisi tek örnekte görülür:

```typescript
class OlcumDefteri {
  ad: string;
  kayitlar: number[] = [];
}

try {
  throw new Error("kayit okunamadi");
} catch (hata) {
  console.log(hata.message);
}
```

```text
t1.ts(2,3): error TS2564: Property 'ad' has no initializer and is not definitely assigned in the constructor.
t1.ts(9,15): error TS18046: 'hata' is of type 'unknown'.
```

İkinci tanı önemli bir kuralı hatırlatır: JavaScript'te `throw` deyimi herhangi bir değeri
atabilir, dolayısıyla yakalanan değerin `Error` olduğu varsayılamaz. Katı yapılandırmada
tip `unknown`dur ve `any`, `unknown` ve `never` dersindeki kural geçerlidir — daraltmadan
kullanılamaz.

Düzeltilmiş biçim:

```typescript
class OlcumDefteri {
  kayitlar: number[] = [];

  constructor(readonly ad: string) {}
}

try {
  throw new Error("kayit okunamadi");
} catch (hata) {
  console.log(hata instanceof Error ? hata.message : String(hata));
}

console.log(new OlcumDefteri("kazan-2").ad);
```

Çıktı:

```text
kayit okunamadi
kazan-2
```

#### Şemsiye Dışındaki Katılık

Bazı katılık seçenekleri `strict` içinde değildir, çünkü var olan kodda çok sayıda tanı
üretirler. Kursta daha önce sözü edilen boşluğu kapatanlar bunlar arasındadır.
Aşağıdaki örnek, yalnızca ilgili seçenekleri taşıyan şu yapılandırmayla derlenir:

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noUnusedLocals": true,
    "noEmit": true
  },
  "include": ["src"]
}
```

Örnek `include` kapsamındaki `src` dizininde durur; dosya adı `src/olcum.ts`'tir:

```typescript
// src/olcum.ts
interface Olcum {
  kimlik: string;
  deger: number;
  not?: string;
}

const kayitlar: Olcum[] = [{ kimlik: "s-01", deger: 21.4 }];

const ilk = kayitlar[0];
console.log(ilk.deger);

const yama: Olcum = { kimlik: "s-02", deger: 22.1, not: undefined };
```

Bu yapılandırmayla derlendiğinde:

```text
src/olcum.ts(11,13): error TS18048: 'ilk' is possibly 'undefined'.
src/olcum.ts(13,7): error TS2375: Type '{ kimlik: string; deger: number; not: undefined; }' is not assignable to type 'Olcum' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
  Types of property 'not' are incompatible.
    Type 'undefined' is not assignable to type 'string'.
src/olcum.ts(13,7): error TS6133: 'yama' is declared but its value is never read.
```

Üç ayrı seçenek üç tanı üretmiştir.

**`noUncheckedIndexedAccess`**, indeksli erişimin sonucuna `undefined` ekler. Arayüzler
dersinde işaret edilen boşluk budur: `kayitlar[0]` ifadesi boş bir dizide `undefined`
verir ve tip bunu artık kaydeder. Bedeli, her indeksli erişimden sonra bir denetim
gerekmesidir; kazancı, dizinin boş olabildiği yerlerin görünür hâle gelmesidir.

**`exactOptionalPropertyTypes`**, seçmeli alanı "yok" ile "değeri `undefined`" arasında
ayırır. Bu ayarla `not?: string` bildirimi, alanın **bulunmayabileceğini** söyler; alana
açıkça `undefined` yazmak ayrı bir şeydir ve reddedilir. İkisinin ayrılması, JSON
dizileştirmesinde önemlidir — bulunmayan alan çıktıda yer almaz, `undefined` değerli alan
da yer almaz ama ayrıştırma sonrası ikisi ayırt edilemez.

**`noUnusedLocals`**, kullanılmayan yerel bildirimleri bildirir. Statik çözümleme
araçlarının işiyle örtüşür; hangisinin sorumlu olacağı proje kararıdır.

Bu üç seçenek yeni projelerde açık başlatılabilir. Var olan bir projeye eklenirken tanı
sayısı yüksek olabilir; kademeli açma stratejisi JavaScript ile Birlikte Çalışma dersinde
ele alınacaktır.

#### Hedef ve Kitaplık

Üç seçenek, kodun hangi ortamda çalışacağını belirler.

**`target`**, üretilecek JavaScript'in dil düzeyini belirler. Değer düşürüldüğünde
derleyici, hedefte bulunmayan sözdizimini karşılığıyla değiştirir — sınıf alanları,
eşzamansız fonksiyonlar ve isteğe bağlı zincirleme dönüştürülür. Bu, ilk derste kurulan
tip silme ilkesinden ayrı bir işlemdir: tipler her hedefte silinir, sözdizim dönüşümü ise
hedefe bağlıdır.

**`lib`**, hangi yerleşik tip bildirimlerinin bulunacağını belirler. `target` değeri
varsayılanı belirler, ancak ayrıca yazılabilir. Tarayıcı arayüzleri kullanılıyorsa DOM
bildirimleri eklenir; sunucu tarafında gerekmez.

**`module`** ve **`moduleResolution`**, modüllerin nasıl üretileceğini ve nasıl
bulunacağını belirler. Modül Çözümleme dersinin konusudur.

Bu üç ayarın ortak yanı, tip katmanına değil **çıktıya** ve **ortama** ait olmalarıdır.
Katılık bayrakları hangi hataların yakalanacağını, bu üçü ise kodun nerede çalışacağını
söyler.

#### Özet

- Yapılandırma dosyası derleyici seçeneklerini ve proje kapsamını belirler;
  `tsc --showConfig` çıktısı varsayılanlarla tamamlanmış hâli ve çözülmüş dosya listesini
  verir.
- `strict` bir şemsiyedir; kapalıyken `null` her tipe girer ve tipsiz parametreler
  denetlenmez.
- Şemsiyenin içeriği sürümlerle büyüyebilir; derleyici yükseltmesinin yeni tanılar
  getirmesi beklenen bir davranıştır.
- `noUncheckedIndexedAccess` indeksli erişime `undefined` ekler,
  `exactOptionalPropertyTypes` alanın yokluğu ile `undefined` değerini ayırır; ikisi de
  şemsiyenin dışındadır.
- `target`, `lib` ve `module` ayarları tip katmanına değil çıktıya ve çalışacağı ortama
  aittir.

#### Sonraki Adım

Yapılandırma, projenin kendi dosyalarını denetlemeyi sağlar. Peki dışarıdan gelen kod —
tip bilgisi taşımayan bir kütüphane, çalışma zamanının sağladığı genel nesneler — nasıl
tiplenir? Sonraki ders bildirim dosyalarını, ortam bildirimlerini ve tip tanımlarının
gerçekleştirimden ayrı taşınmasını ele alacak.

### Bildirim Dosyaları

Source: https://academia.sh/tr/kurslar/typescript/bildirim-dosyalari

Tip tanımlarının gerçekleştirimden ayrı taşınması; kaynaktan bildirim üretme, ortam modülü bildirimleri, genel kapsam bildirimi ve dış tiplerin birleştirmeyle genişletilmesi.

Önceki ders, projenin kendi dosyalarını denetlemeyi sağladı. Dışarıdan gelen kod — tip
bilgisi taşımayan bir kütüphane, çalışma zamanının sağladığı genel nesneler — henüz
kapsam dışında.

**Bildirim dosyası** (declaration file), yalnızca tip bilgisi taşıyan ve hiçbir kod
üretmeyen dosyadır. Uzantısı `.d.ts`tir. İki yönde kullanılır: kendi kodunuzun tiplerini
dışa vermek ve dışarıdaki kodun tiplerini içeri almak.

#### Kaynaktan Bildirim Üretme

Bir kütüphane paketi, gerçekleştirimini JavaScript olarak yayımlar. TypeScript kullanan
tüketicilerin tip bilgisine erişebilmesi için bildirim dosyalarının da yayımlanması
gerekir. Derleyici bunları kaynaktan üretir:

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "declaration": true,
    "rootDir": "src",
    "outDir": "dist"
  },
  "include": ["src"]
}
```

Proje kökünde şu içerikte bir `package.json` bulunsun:

```json
// package.json
{
  "name": "olcum-kitapligi",
  "type": "module",
  "version": "1.0.0"
}
```

Ve `src/olcum.ts` dosyası şu olsun:

```typescript
// src/olcum.ts
export type Birim = "C" | "Pa" | "%";

export interface Olcum {
  readonly kimlik: string;
  deger: number;
  birim: Birim;
}

export function ortalama(kayitlar: readonly Olcum[]): number {
  if (kayitlar.length === 0) {
    return 0;
  }
  return kayitlar.reduce((t, k) => t + k.deger, 0) / kayitlar.length;
}

export const varsayilanBirim = "C";
```

Proje dizininde `tsc` çalıştırıldığında iki dosya üretilir. Blokların ilk satırındaki `// …`
yorumu yalnızca dosyanın adını gösterir, gerçek dosyanın parçası değildir. `dist/olcum.js`:

```javascript
// dist/olcum.js
export function ortalama(kayitlar) {
    if (kayitlar.length === 0) {
        return 0;
    }
    return kayitlar.reduce((t, k) => t + k.deger, 0) / kayitlar.length;
}
export const varsayilanBirim = "C";
```

Ve `dist/olcum.d.ts`:

```typescript
// dist/olcum.d.ts
export type Birim = "C" | "Pa" | "%";
export interface Olcum {
    readonly kimlik: string;
    deger: number;
    birim: Birim;
}
export declare function ortalama(kayitlar: readonly Olcum[]): number;
export declare const varsayilanBirim = "C";
```

İkisi birbirini tamamlar. JavaScript dosyasında tipler yoktur, gerçekleştirim vardır;
bildirim dosyasında gerçekleştirim yoktur, tipler vardır. `declare` sözcüğü tam olarak bunu
söyler: "böyle bir ad var, tipi budur, tanımı burada değil."

Bu ayrım, ilk dersteki tip silme ilkesinin paket düzeyindeki karşılığıdır. Tip katmanı
yayınlanan koddan ayrılmıştır; onu okumayan bir tüketici için paket sıradan bir JavaScript
paketidir.

Bu dersteki `rootDir` ayarı, bu derleyici sürümünde `outDir` ile birlikte açıkça
yazılmalıdır; yazılmadığında `TS5011` tanısı gelir. Ayarın işlevi çıktı dizin düzenini
belirlemektir: `src/olcum.ts` dosyası `dist/olcum.js` olur, `dist/src/olcum.js` değil.

#### Ortam Modülü Bildirimi

Tip bilgisi taşımayan bir kütüphane kullanıldığında, tipleri projede yazılabilir. Buna
**ortam bildirimi** (ambient declaration) denir — gerçekleştirimi başka yerde olan bir
şeyin tipini bildirmek.

Yapılandırmaya `tipler` dizini eklensin; bu yapılandırma öncekinin yerine geçer:

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "noEmit": true
  },
  "include": ["src", "tipler"]
}
```

`tipler/olcum-surucusu.d.ts` dosyası, kütüphanenin sunduğu adları bildirir:

```typescript
// tipler/olcum-surucusu.d.ts
declare module "olcum-surucusu" {
  export interface SurucuAyarlari {
    adres: string;
    zamanAsimi?: number;
  }

  export function baglan(ayarlar: SurucuAyarlari): Promise<void>;
  export function oku(kanal: string): Promise<number>;
}
```

Bu bildirimden sonra `src/uygulama.ts` içinde modül tiplenmiş olarak kullanılabilir:

```typescript
// src/uygulama.ts
import { baglan, oku } from "olcum-surucusu";

export async function ilkOkuma(): Promise<number> {
  await baglan({ adres: "10.0.0.7:9000" });
  return oku("sicaklik.C");
}

export async function hataliCagri(): Promise<number> {
  await baglan({ adres: "10.0.0.7:9000", yenidenDene: 3 });
  return oku(42);
}
```

```text
src/uygulama.ts(10,42): error TS2353: Object literal may only specify known properties, and 'yenidenDene' does not exist in type 'SurucuAyarlari'.
src/uygulama.ts(11,14): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
```

Elle yazılmış bildirim, derleyici için gerçek bir sözleşmedir ve tüm denetimler uygulanır.

Bir uyarı gerekir: bu bildirimin doğruluğu sınanmaz. Kütüphane gerçekte farklı davranıyorsa
derleyici bunu bilemez. Ortam bildirimi, `as` yazımı ve tip yüklemi gibi bir **borçtur**;
kütüphane sürümü değiştiğinde birlikte güncellenmesi gerekir. Bu nedenle, kütüphanenin
kendi bildirimlerini yayımladığı durumlarda elle yazılmış bildirim tercih edilmez.

#### Genel Kapsam Bildirimi

Bazı adlar bir modülden gelmez; çalışma zamanı tarafından genel kapsama konur. `declare
global` bloğu bunları `tipler/ortam.d.ts` dosyasında bildirir:

```typescript
// tipler/ortam.d.ts
declare global {
  interface OlcumOrtami {
    readonly dugumAdi: string;
    readonly ornekleme: number;
  }

  const olcumOrtami: OlcumOrtami;
}

export {};
```

Sondaki `export {};` satırı gereklidir: dosyanın bir modül sayılmasını sağlar ve `declare
global` bloğu yalnızca modül dosyalarında yazılabilir.

Bildirimden sonra ad, `import` yazılmadan kullanılabilir:

```typescript
// src/ortam.ts
export function ortamOzeti(): string {
  return `${olcumOrtami.dugumAdi} @ ${olcumOrtami.ornekleme}Hz`;
}

export function hataliErisim(): string {
  return olcumOrtami.bilinmeyenAlan;
}
```

```text
src/ortam.ts(7,22): error TS2339: Property 'bilinmeyenAlan' does not exist on type 'OlcumOrtami'.
```

Genel kapsam bildirimi güçlü ama dikkatli kullanılması gereken bir araçtır: bildirilen ad
projenin her dosyasında görünür olur ve nereden geldiği kod okunarak anlaşılamaz. Bir adın
gerçekten genel olması gerekmiyorsa, modülden dışa aktarmak yeğlenir.

#### Dış Tipleri Genişletme

Arayüzler dersinde tanıtılan bildirim birleştirme, asıl işlevini burada bulur. Bir
kütüphanenin tipi, kütüphaneye dokunmadan genişletilebilir.

Genişletme yokken `SurucuAyarlari` arayüzünde `yenidenDeneme` alanı bulunmaz. Alan adı
`yenidenDene` biçiminde yanlış yazıldığında `src/uygulama.ts` dosyası şu tanıyı alır:

```typescript
// src/uygulama.ts
import { baglan, oku } from "olcum-surucusu";

export async function ilkOkuma(): Promise<number> {
  await baglan({ adres: "10.0.0.7:9000", yenidenDene: 3 });
  return oku("sicaklik.C");
}
```

```text
src/uygulama.ts(5,42): error TS2353: Object literal may only specify known properties, and 'yenidenDene' does not exist in type 'SurucuAyarlari'.
```

Şimdi `tipler/genisletme.d.ts` dosyası aynı modül adıyla ikinci bir blok açsın:

```typescript
// tipler/genisletme.d.ts
declare module "olcum-surucusu" {
  interface SurucuAyarlari {
    yenidenDeneme?: number;
  }
}
```

Aynı modül adını taşıyan ortam bildirimleri birleşir; `SurucuAyarlari` arayüzü de arayüz
olduğu için birleştirmeye açıktır. Sonuçta alan, asıl bildirim hiç değişmeden kullanılabilir
hâle gelir:

```typescript
// src/uygulama.ts
import { baglan, oku } from "olcum-surucusu";

export async function ilkOkuma(): Promise<number> {
  await baglan({ adres: "10.0.0.7:9000", yenidenDeneme: 3 });
  return oku("sicaklik.C");
}

export function ortamOzeti(): string {
  return `${olcumOrtami.dugumAdi} @ ${olcumOrtami.ornekleme}Hz`;
}
```

Bu dosya, önceki üç bildirim dosyasıyla birlikte hatasız denetlenir.

Birleştirmenin gerçekten uygulandığı, alan adı yanlış yazıldığında görülür. Genişletme
projedeyken aynı dosyada `yenidenDeneme` yerine `yenidenDene` yazıldığında:

```typescript
// src/uygulama.ts
import { baglan, oku } from "olcum-surucusu";

export async function ilkOkuma(): Promise<number> {
  await baglan({ adres: "10.0.0.7:9000", yenidenDene: 3 });
  return oku("sicaklik.C");
}
```

```text
src/uygulama.ts(5,42): error TS2561: Object literal may only specify known properties, but 'yenidenDene' does not exist in type 'SurucuAyarlari'. Did you mean to write 'yenidenDeneme'?
```

İki tanı arasındaki fark — genişletme yokken öneri yok, varken var — birleştirmenin
uygulanıp uygulanmadığını doğrudan gösterir.

Genişletmenin sınırı, yalnızca **arayüzlerde** çalışmasıdır. Kütüphane tipini `type` ile
tanımlamışsa genişletilemez; Tip Takma Adları dersindeki `TS2300` tanısı gelir. Kütüphane
yazarları için pratik sonuç şudur: dışa açılan nesne biçimleri arayüzle bildirilirse
tüketiciler genişletebilir.

#### Özet

- Bildirim dosyası yalnızca tip taşır; `declare` sözcüğü bir adın var olduğunu ama
  tanımının başka yerde bulunduğunu söyler.
- `declaration` seçeneği bildirim dosyalarını kaynaktan üretir; bu derleyici sürümünde
  `outDir` ile birlikte `rootDir` de yazılmalıdır.
- Ortam modülü bildirimi, tip bilgisi taşımayan bir kütüphaneyi tipler; doğruluğu
  sınanmaz ve kütüphaneyle birlikte güncellenmesi gereken bir borçtur.
- `declare global` bloğu genel kapsamdaki adları bildirir; dosyanın modül olması için
  `export {};` satırı gerekir.
- `import` satırı taşıyan bir `declare module` bloğu var olan modülün tiplerini
  genişletir; genişletme yalnızca arayüzlerde çalışır.

#### Sonraki Adım

Ortam bildirimi, tipsiz bir kütüphaneyi dışarıdan tiplemenin yoluydu. Peki projenin
**kendi** JavaScript dosyaları? Var olan bir kod tabanı bir gecede TypeScript'e
çevrilemez; bu geçişin kademeli yapılması gerekir. Sonraki ders JavaScript dosyalarının
denetlenmesini, açıklamalarla tipleme yöntemini ve geçiş stratejisini ele alacak.

### JavaScript ile Birlikte Çalışma

Source: https://academia.sh/tr/kurslar/typescript/javascript-ile-birlikte-calisma

JavaScript dosyalarının projeye alınması ve denetlenmesi, açıklamalarla tipleme, dosya düzeyinde denetim yönergeleri ve kademeli geçiş stratejisi.

Önceki ders, tipsiz bir kütüphaneyi dışarıdan tiplemenin yolunu kurdu. Projenin **kendi**
JavaScript dosyaları başka bir sorundur: var olan bir kod tabanı bir gecede
dönüştürülemez.

TypeScript bu geçişi destekleyecek biçimde tasarlanmıştır. Her JavaScript dosyası aynı
zamanda geçerli bir TypeScript girdisidir; denetim düzeyi ise proje ve dosya bazında
ayarlanabilir. Bu ders geçişin araçlarını ve sırasını ele alır.

#### JavaScript Dosyalarını Projeye Almak

İki ayrı seçenek vardır ve karıştırılmaları sık görülür.

**`allowJs`**, JavaScript dosyalarının projeye alınmasını sağlar: derleyici onları okur,
dışa aktardıkları adları bilir ve TypeScript dosyalarından içe aktarılmalarına izin verir.
**`checkJs`**, bu dosyaların tip denetiminden geçmesini sağlar.

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "allowJs": true,
    "checkJs": false,
    "noEmit": true
  },
  "include": ["src"]
}
```

Proje kökünde şu içerikte bir `package.json` bulunsun:

```json
// package.json
{
  "name": "olcum-gecis",
  "type": "module",
  "version": "1.0.0"
}
```

`src/eski.js`, geçiş öncesi koddur:

```javascript
// src/eski.js
export function ortalama(kayitlar) {
  let toplam = 0;
  for (const k of kayitlar) {
    toplam += k.deger;
  }
  return toplam / kayitlar.length;
}
```

`src/yeni.ts` onu kullanır:

```typescript
// src/yeni.ts
import { ortalama } from "./eski.js";

const kayitlar = [{ deger: 21.4 }, { deger: 22.1 }];
const sonuc: number = ortalama(kayitlar);
console.log(sonuc.toFixed(1));
```

Bu yapılandırmayla derleme hatasız geçer. Ancak güvence yoktur: `ortalama` fonksiyonunun
parametresi ve dönüş değeri `any` sayılır, dolayısıyla `sonuc` bildirimi hiçbir şey
sınamaz.

`checkJs` açıldığında yapılandırma şu olur:

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "allowJs": true,
    "checkJs": true,
    "noEmit": true
  },
  "include": ["src"]
}
```

Aynı `src/yeni.ts` bu yapılandırmayla yeniden denetlendiğinde tanı, JavaScript dosyası için
bildirilir:

```typescript
// src/yeni.ts
import { ortalama } from "./eski.js";

const kayitlar = [{ deger: 21.4 }, { deger: 22.1 }];
const sonuc: number = ortalama(kayitlar);
console.log(sonuc.toFixed(1));
```

```text
src/eski.js(2,26): error TS7006: Parameter 'kayitlar' implicitly has an 'any' type.
```

Katı yapılandırma artık JavaScript dosyasına da uygulanır. Büyük bir kod tabanında bu
adım yüzlerce tanı üretir; bu nedenle geçiş genellikle dosya dosya yapılır.

#### Açıklamalarla Tipleme

JavaScript dosyaları, sözdizimlerini değiştirmeden tiplenebilir. Derleyici, belirli bir
biçimdeki açıklama bloklarını tip bildirimi olarak okur:

```javascript
// src/eski.js
/**
 * @typedef {object} Olcum
 * @property {string} kimlik
 * @property {number} deger
 */

/**
 * @param {readonly Olcum[]} kayitlar
 * @returns {number}
 */
export function ortalama(kayitlar) {
  let toplam = 0;
  for (const k of kayitlar) {
    toplam += k.deger;
  }
  return toplam / kayitlar.length;
}
```

Bu dosya `"checkJs": true` ile hatasız denetlenir ve dışa aktardığı fonksiyon artık
tiplenmiştir. Kullanan TypeScript dosyası:

```typescript
// src/yeni.ts
import { ortalama } from "./eski.js";

const kayitlar = [
  { kimlik: "s-01", deger: 21.4 },
  { kimlik: "s-02", deger: 22.1 },
];
const sonuc: number = ortalama(kayitlar);
console.log(sonuc.toFixed(1));

ortalama([{ kimlik: "s-03", deger: "23.0" }]);
```

```text
src/yeni.ts(11,29): error TS2322: Type 'string' is not assignable to type 'number'.
```

Tanı, JavaScript dosyasındaki açıklamadan gelen sözleşmenin gerçekten uygulandığını
gösterir. `@typedef`, `@param` ve `@returns` yönergeleri kursta kurulan tip yazımlarının
açıklama içindeki karşılığıdır; birleşim, dizi ve genellik yazımları da desteklenir.

Bu yöntemin bedeli okunabilirliktir: aynı bilgi daha uzun yazılır ve gövdeden ayrı durur.
Kazancı, dosyanın hâlâ sıradan bir JavaScript dosyası olmasıdır — derleme adımı
gerektirmeden çalışır. Derleme adımı bulunmayan projelerde ya da geçişin ilk aşamasında
uygun bir ara çözümdür.

#### Dosya Düzeyinde Denetim Yönergeleri

Denetim, dosyanın ilk satırlarına konan bir açıklamayla da açılıp kapatılabilir.
`"checkJs": false` iken bir dosyaya `// @ts-check` yazmak, yalnızca o dosyayı denetime alır. Bu
bölüm için yapılandırma yeniden `checkJs` kapalı hâle getirilir:

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "allowJs": true,
    "checkJs": false,
    "noEmit": true
  },
  "include": ["src"]
}
```

Ve `src/kontrol.js` dosyası şu olsun:

```javascript
// src/kontrol.js
// @ts-check

/**
 * @typedef {object} Olcum
 * @property {string} kimlik
 * @property {number} deger
 */

/**
 * @param {readonly Olcum[]} kayitlar
 * @returns {number}
 */
export function ortalama(kayitlar) {
  let toplam = 0;
  for (const k of kayitlar) {
    toplam += k.kimlik;
  }
  return toplam / kayitlar.length;
}
```

Bu dosyayı içe alan bir TypeScript dosyası derlendiğinde tanı, JavaScript dosyasının
kendisi için bildirilir:

```typescript
// src/kullanim.ts
import { ortalama } from "./kontrol.js";

console.log(ortalama([{ kimlik: "s-01", deger: 21.4 }]).toFixed(1));
```

```text
src/kontrol.js(17,5): error TS2322: Type 'string' is not assignable to type 'number'.
```

Ters yönergesi `// @ts-nocheck`tir ve `"checkJs": true` iken bir dosyayı denetim dışında
bırakır.

İki yönergenin kullanım yeri farklıdır. `// @ts-check`, geçişin başında az sayıda dosyayı
denetime almak içindir — proje ayarı kapalı, dosyalar tek tek açılır. `// @ts-nocheck`,
geçişin sonunda kalan birkaç dosyayı geçici olarak dışarıda bırakmak içindir — proje ayarı
açık, dosyalar tek tek muaf tutulur. İkinci düzen yeğlenir: varsayılan güvenli, muafiyet
görünürdür.

#### Tek Satırlık Muafiyet

Bazen bir dosyanın tamamı değil, tek bir satır sorunludur. İki yönerge vardır ve
aralarındaki fark önemlidir.

`// @ts-ignore`, sonraki satırdaki tüm tanıları bastırır ve bastıracak bir tanı olmasa da
sessiz kalır. `// @ts-expect-error` ise sonraki satırda bir tanı **beklediğini** söyler;
beklenen tanı gelmezse kendisi tanı üretir:

```typescript
// src/yeni.ts
import { ortalama } from "./eski.js";

const kayitlar = [
  { kimlik: "s-01", deger: 21.4 },
  { kimlik: "s-02", deger: 22.1 },
];
const sonuc: number = ortalama(kayitlar);
console.log(sonuc.toFixed(1));

// @ts-expect-error dizgi deger kabul edilmez
ortalama([{ kimlik: "s-03", deger: "23.0" }]);

// @ts-expect-error bu satir aslinda gecerli
ortalama([{ kimlik: "s-04", deger: 24.0 }]);
```

```text
src/yeni.ts(14,1): error TS2578: Unused '@ts-expect-error' directive.
```

On birinci satırdaki yönerge doğrudur ve tanı üretmez; on dördüncü satırdaki yönerge gereksizdir
ve bildirilir.

Bu davranış, `// @ts-expect-error` yönergesini `// @ts-ignore` karşısında üstün kılar.
Bastırılan bir hata sonradan düzeltildiğinde — kütüphane güncellendiğinde, tip tanımı
iyileştirildiğinde — `@ts-ignore` sessizce kalır ve kimse fark etmez. `@ts-expect-error`
ise "artık gerekli değilim" der. Muafiyetler böylece kendiliğinden temizlenir.

Her iki yönergenin de yanına gerekçe yazılır; yukarıdaki örnekte olduğu gibi yönerge
metninin devamı açıklama olarak kabul edilir.

#### Kademeli Geçiş Sırası

Toplanan araçlar bir sıraya konabilir. Her adım, önceki adımın sağladığı güvenceyi
korur:

1. **`allowJs` açılır, `checkJs` kapalı kalır.** JavaScript dosyaları projeye girer; hiçbir
   yeni tanı gelmez. Yeni yazılan kod TypeScript olur.
2. **Sık kullanılan modüllerin sözleşmeleri yazılır.** Ya `.d.ts` dosyasıyla ya da
   açıklamalarla. En çok içe aktarılan modüller önce ele alınır; her biri, kendisini
   kullanan tüm dosyalara güvence yayar.
3. **Dosyalar tek tek `// @ts-check` ile denetime alınır.** Çıkan tanılar düzeltilir.
   Düzeltilemeyenler `// @ts-expect-error` ile gerekçelendirilir.
4. **`checkJs` açılır, kalan dosyalar `// @ts-nocheck` ile muaf tutulur.** Varsayılan
   artık güvenli taraftadır.
5. **Dosyalar `.ts` uzantısına taşınır** ve açıklamadaki tipler sözdizime çevrilir.
6. **Şemsiye dışındaki katılık bayrakları teker teker açılır.** Derleyici Yapılandırması
   dersindeki `noUncheckedIndexedAccess` ve `exactOptionalPropertyTypes` bu aşamada gelir.

Sıranın ilkesi, her adımda **kırmızının sıfırda tutulmasıdır**: derleme her zaman temiz
geçmeli, yeni tanılar biriktirilmemelidir. Aksi hâlde tanı listesi bir gürültüye dönüşür ve
gerçek hatalar arasında kaybolur.

Bir noktada `any` kullanımı kaçınılmazdır. Kaçış kapıları dersindeki kural burada
uygulanır: `any` bir geçiş işaretidir. Nerede kaldığı belgelenir ve geçişin sonraki
turlarında daraltılır.

#### Özet

- `allowJs` JavaScript dosyalarını projeye alır, `checkJs` onları tip denetimine sokar;
  ikisi ayrı kararlardır.
- Açıklama yazımıyla (`@typedef`, `@param`, `@returns`) JavaScript dosyaları sözdizimleri
  değişmeden tiplenir; sözleşme TypeScript tarafında da uygulanır.
- `// @ts-check` ve `// @ts-nocheck` denetimi dosya düzeyinde açar ve kapatır; muafiyetin
  görünür olduğu düzen yeğlenir.
- `// @ts-expect-error`, beklenen tanı gelmediğinde `TS2578` üretir ve gereksiz
  muafiyetlerin temizlenmesini sağlar; `// @ts-ignore` bunu yapmaz.
- Kademeli geçiş, her adımda derlemeyi temiz tutarak ilerler: dosyaları almak, sözleşmeleri
  yazmak, denetimi açmak, uzantıyı taşımak, katılığı artırmak.

#### Sonraki Adım

Bu dersteki içe aktarma satırı `./eski.js` yazıyordu ve uzantı zorunluydu. Aynı yazım,
kaynak bir TypeScript dosyası olduğunda da değişmez — bu, modül çözümlemenin kurallarından
biridir ve ilk bakışta şaşırtıcıdır. Sonraki ders derleyicinin bir modül adını nasıl bir
dosyaya çevirdiğini, yol eşlemeyi ve çözümleme ayarlarının çıktı üzerindeki etkisini ele
alacak.

### Modül Çözümleme

Source: https://academia.sh/tr/kurslar/typescript/modul-cozumleme

Derleyicinin modül adını dosyaya çevirme kuralları, uzantı gerekliliği, çözümleme stratejileri, yol eşlemenin çıktı üzerindeki sınırı ve paket iç yollarıyla çözüm.

Önceki dersteki içe aktarma satırı `./eski.js` yazıyordu ve uzantı zorunluydu. Orada
içe aktarılan dosya gerçekten bir JavaScript dosyasıydı; aynı yazım, kaynak bir TypeScript
dosyası olduğunda da değişmez.

Bu, **modül çözümlemenin** (module resolution) kurallarından biridir: derleyicinin bir
modül adını bir dosyaya çevirme süreci. Kurallar `moduleResolution` ayarına, dosyanın modül
türüne ve paketin bildirimine bağlıdır. Bu ders süreci ve tuzaklarını ele alır.

#### Uzantı Gerekliliği

Bir proje düşünelim. `package.json` dosyası şu olsun:

```json
// package.json
{
  "name": "olcum-modulleri",
  "type": "module",
  "version": "1.0.0"
}
```

Yapılandırma da şu olsun:

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "rootDir": "src",
    "outDir": "dist"
  },
  "include": ["src"]
}
```

`src/veri/olcum.ts` kursun modelini dışa aktarsın:

```typescript
// src/veri/olcum.ts
export interface Olcum {
  kimlik: string;
  deger: number;
}

export function ortalama(kayitlar: readonly Olcum[]): number {
  return kayitlar.length === 0
    ? 0
    : kayitlar.reduce((t, k) => t + k.deger, 0) / kayitlar.length;
}
```

`src/ana.ts` içinde üç farklı yazım denenebilir. Uzantısız:

```typescript
// src/ana.ts
import { ortalama } from "./veri/olcum";

console.log(ortalama([{ kimlik: "s-01", deger: 21.4 }]));
```

```text
src/ana.ts(2,26): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './veri/olcum.js'?
```

Kaynak dosyanın gerçek adıyla, yani `.ts` uzantısıyla:

```typescript
// src/ana.ts
import { ortalama } from "./veri/olcum.ts";

console.log(ortalama([{ kimlik: "s-01", deger: 21.4 }]));
```

```text
src/ana.ts(2,26): error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.
```

Ve derleyicinin önerdiği biçimde:

```typescript
// src/ana.ts
import { ortalama } from "./veri/olcum.js";

console.log(ortalama([{ kimlik: "s-01", deger: 21.4 }]));
```

Bu üçüncü biçim derlenir. Blokların ilk satırındaki `// …` yorumu yalnızca dosyanın adını
gösterir, gerçek dosyanın parçası değildir. Üretilen `dist/ana.js`:

```javascript
// dist/ana.js
import { ortalama } from "./veri/olcum.js";
console.log(ortalama([{ kimlik: "s-01", deger: 21.4 }]));
```

`node dist/ana.js` çıktısı `21.4`tür.

Kural şaşırtıcıdır ve nedeni tip silme ilkesinde yatar: **derleyici içe aktarma yollarını
değiştirmez.** Kaynakta ne yazıyorsa çıktıda o kalır. Çalışacak olan JavaScript dosyası
`olcum.js` olduğuna göre, kaynakta da o yazılmalıdır. `.ts` uzantısı yazmak, çıktıda
bulunmayan bir dosyaya başvurmak demektir.

Bu davranışı değiştiren seçenekler bulunur: `.ts` uzantısıyla yazmaya izin veren ve
gerektiğinde çıktıda uzantıyı yeniden yazan bayraklar. Hangilerinin bulunduğu derleyici
sürümüne bağlıdır; ilkeyi bilmek, hangi bayrağın ne yaptığını anlamayı sağlar.

#### Çözümleme Stratejileri

`moduleResolution` ayarı, adın hangi kurallarla aranacağını belirler. Üç sınıf vardır.

**Çalışma zamanı kuralına uyan stratejiler** (`node16`, `nodenext`), sunucu çalışma
zamanının modül çözümleme algoritmasını taklit eder: uzantı zorunludur, paket bildirimi
okunur, dosyanın modül mü betik mi olduğu `package.json` içindeki tür alanına göre
belirlenir. Yukarıdaki tanılar bu sınıftan gelir.

**Paketleyici kuralına uyan strateji** (`bundler`), derleme adımında bir paketleyicinin
bulunduğunu varsayar. Uzantı gerekmez, çünkü yolu çözecek olan derleyici değil
paketleyicidir:

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "preserve",
    "moduleResolution": "bundler",
    "strict": true,
    "noEmit": true
  },
  "include": ["src"]
}
```

Bu yapılandırmayla uzantısız yazım hatasız denetlenir:

```typescript
// src/ana.ts
import { ortalama } from "./veri/olcum";

console.log(ortalama([{ kimlik: "s-01", deger: 21.4 }]));
```

**Eski strateji** (`node10`), paket bildirimindeki ayrıntıları okumayan ilk kuşak
algoritmadır ve yeni projelerde seçilmez.

Seçim ölçütü şudur: **kodu kim çalıştıracaksa onun kuralı seçilir.** Çıktı doğrudan bir
çalışma zamanına veriliyorsa çalışma zamanı stratejisi, bir paketleyiciden geçiyorsa
paketleyici stratejisi. Yanlış seçimin belirtisi, tip denetiminin geçip çalıştırmanın
başarısız olmasıdır.

#### Paket Adları ve Tip Bildirimlerinin Bulunması

Göreli olmayan bir ad — bir paket adı — farklı bir yolla çözülür: derleyici, dosya
ağacında yukarı doğru ilerleyerek paket dizinlerini arar ve bulduğu paketin bildirimini
okur.

Bu bölümde önceki yapılandırma geçerlidir ve paket `node_modules/olcum-surucusu` altına
kurulmuştur. Paket bildirimi, aynı ad için hem çalışma zamanı dosyasını hem tip dosyasını
gösterebilir:

```json
// node_modules/olcum-surucusu/package.json
{
  "name": "olcum-surucusu",
  "version": "1.0.0",
  "type": "module",
  "exports": {
    ".": {
      "types": "./dist/surucu.d.ts",
      "default": "./dist/surucu.js"
    }
  }
}
```

`types` koşulu derleyiciye, `default` koşulu çalışma zamanına yanıt verir. Paket
`dist/surucu.d.ts` dosyasında şu bildirimi yayımlasın:

```typescript
// node_modules/olcum-surucusu/dist/surucu.d.ts
export declare function oku(kanal: string): Promise<number>;
```

Bu durumda tüketici tarafında sözleşme uygulanır:

```typescript
// src/ana.ts
import { oku } from "olcum-surucusu";

export async function ilkOkuma(): Promise<number> {
  return oku("sicaklik.C");
}

export async function hataliOkuma(): Promise<number> {
  return oku(42);
}
```

```text
src/ana.ts(9,14): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
```

Çözümlemenin hangi adımlardan geçtiği sorulabilir. `--traceResolution` seçeneği bunu
yazar; aşağıdaki çıktıda proje kökünün mutlak yolu `.../paket` biçiminde kısaltılmıştır:

```text
======== Resolving module 'olcum-surucusu' from '.../paket/src/ana.ts'. ========
Loading module 'olcum-surucusu' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON.
Found 'package.json' at '.../paket/node_modules/olcum-surucusu/package.json'.
File '.../paket/node_modules/olcum-surucusu/dist/surucu.d.ts' exists - use it as a name resolution result.
======== Module name 'olcum-surucusu' was successfully resolved to '.../paket/node_modules/olcum-surucusu/dist/surucu.d.ts' with Package ID 'olcum-surucusu/dist/surucu.d.ts@1.0.0'. ========
```

Çıktı, çözümlemenin nerede takıldığını bulmanın en doğrudan yoludur: "bu paketin tipleri
neden görünmüyor" sorusu, hangi dosyanın arandığı ve bulunamadığı satırlarla yanıtlanır.

Paket kendi bildirimlerini yayımlamıyorsa iki seçenek kalır. Birincisi, topluluk
tarafından ayrı bir pakette yayımlanan bildirimleri kurmaktır; bu paketler bir ad öneki
altında toplanır ve derleyici onları kendiliğinden bulur. İkincisi, Bildirim Dosyaları
dersindeki ortam bildirimini yazmaktır. İkisi de aynı borcu taşır: bildirim, paketin
gerçek davranışıyla elle uyumlu tutulur.

#### Yol Eşleme ve Sınırı

Derin dizin yapılarında göreli yollar okunaksızlaşır. `paths` ayarı, ad ile dosya arasında
bir eşleme kurar:

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "rootDir": "src",
    "outDir": "dist",
    "paths": {
      "@veri/*": ["./src/veri/*"]
    }
  },
  "include": ["src"]
}
```

```typescript
// src/ana.ts
import { ortalama } from "@veri/olcum.js";

console.log(ortalama([{ kimlik: "s-01", deger: 21.4 }]));
```

Bu dosya tip denetiminden **hatasız geçer**. Üretilen `dist/ana.js`:

```javascript
// dist/ana.js
import { ortalama } from "@veri/olcum.js";
console.log(ortalama([{ kimlik: "s-01", deger: 21.4 }]));
```

`node dist/ana.js` çalıştırıldığında çalışma zamanı `ERR_MODULE_NOT_FOUND` koduyla durur:
`@veri/olcum.js` adında bir paket bulunamaz.

Neden bir öncekiyle aynıdır: derleyici içe aktarma yollarını değiştirmez. `paths` ayarı
yalnızca **derleyicinin** adı çözmesini sağlar; çalışma zamanının aynı eşlemeden haberi
yoktur.

Bu, kursun ilk dersinde kurulan ayrımın son ve en pahalı örneğidir. Tip katmanı derleme
anına aittir; çalışma zamanı davranışını değiştirmez. `paths` bir tip katmanı ayarıdır,
bir modül sistemi ayarı değil.

Ayrıca `paths` ile birlikte sık kullanılan `baseUrl` ayarı bu derleyici sürümünde
kullanımdan kaldırılmış sayılır ve yazıldığında `TS5101` tanısı gelir. Yol desenleri artık
doğrudan yapılandırma dosyasının bulunduğu dizine göre yazılır.

#### Doğru Çözüm: Paket İç Yolları

Aynı okunabilirlik, çalışma zamanının da bildiği bir yolla elde edilir. Paket bildirimine
iç yol eşlemesi yazılır:

```json
// package.json
{
  "name": "olcum-modulleri",
  "type": "module",
  "version": "1.0.0",
  "imports": {
    "#veri/*": "./dist/veri/*"
  }
}
```

Yapılandırmadan `paths` kaldırılır:

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "rootDir": "src",
    "outDir": "dist"
  },
  "include": ["src"]
}
```

İçe aktarma da şu biçimi alır:

```typescript
// src/ana.ts
import { ortalama } from "#veri/olcum.js";

console.log(ortalama([{ kimlik: "s-01", deger: 21.4 }]));
```

Derleme hatasız geçer, `dist/ana.js` çıktısı içe aktarmayı olduğu gibi korur ve
`node dist/ana.js` çıktısı `21.4` olur.

Fark şudur: `#veri/*` deseni derleyicinin değil **paket bildiriminin** bir özelliğidir.
`nodenext` stratejisi paket bildirimini okuduğu için derleyici de aynı eşlemeyi kullanır.
Tek bir kaynak, iki tüketici.

Genel ilke: **çözümleme kuralı, çalışma zamanının anlayacağı yerde tanımlanır.** Yalnızca
derleyicinin bildiği bir kural, çıktı ile kaynak arasında bir uçurum açar.

Paketleyici kullanan projelerde durum farklıdır: orada yolları çözen paketleyicidir ve
`paths` ayarı, paketleyicinin kendi eşlemesiyle **aynı** tutulduğu sürece güvenlidir. İki
yerde tutulan bir bilginin ayrışması ise bilinen bir bakım sorunudur.

#### Özet

- Derleyici içe aktarma yollarını değiştirmez; kaynakta yazılan ad çıktıda aynen kalır.
- Çalışma zamanı stratejilerinde göreli yollar uzantı ister ve uzantı, kaynağın değil
  çıktının adıdır; `.ts` yazımı `TS5097`, uzantısız yazım `TS2835` tanısı verir.
- Paketleyici stratejisinde uzantı gerekmez, çünkü yolu paketleyici çözer; strateji, kodu
  çalıştıracak olana göre seçilir.
- `paths` ayarı yalnızca derleyicinin çözümlemesini etkiler; çalışma zamanı aynı eşlemeyi
  bilmediği için çıktı çalışmayabilir.
- Paket bildirimindeki iç yol eşlemesi hem derleyici hem çalışma zamanı tarafından okunur
  ve tek kaynaklı bir çözüm verir.

#### Sonraki Adım

Kurs boyunca derleyici hem tip denetimi yaptı hem çıktı üretti. Bu iki işin ayrılması,
büyük projelerde derleme süresini ve dağıtım düzenini belirleyen bir karardır. Son ders bu
ayrımı, hata durumunda yayın davranışını ve kursun kurduğu tüm katmanları bir arada ele
alacak.

### Derleme ve Çalıştırma Akışı

Source: https://academia.sh/tr/kurslar/typescript/derleme-ve-calistirma-akisi

Tip denetimi ile yayın çıktısının ayrılması, hata durumunda yayın davranışı, dosya başına çeviriyi olanaklı kılan seçenekler, tip-yalnızca içe aktarma ve tip soyan çalışma zamanları.

Kurs boyunca derleyici hem tip denetimi yaptı hem çıktı üretti. Bu iki işin ayrılması,
projelerin derleme süresini ve dağıtım düzenini belirleyen bir karardır.

Ayrımın gerekçesi ilk derste kurulmuştu: tipler derleme anında silinir. Silme işlemi
mekanik ve hızlıdır; tip denetimi ise programın tümünü çözümlemeyi gerektirir ve
yavaştır. İki işi ayırmak, geliştirme döngüsünü kısaltır.

#### Hata Durumunda Yayın

İlk derste görülen davranışı bir proje bağlamında yeniden inceleyelim. `src/ana.ts`:

```typescript
const deger: number = "21.4";
console.log(deger);
```

Yapılandırma `outDir` ile çıktı üretecek biçimde ayarlanmışsa, derleme şu tanıyı verir:

```text
src/ana.ts(1,7): error TS2322: Type 'string' is not assignable to type 'number'.
```

ve buna rağmen `dist/ana.js` dosyasını yazar:

```javascript
const deger = "21.4";
console.log(deger);
export {};
```

`noEmitOnError` seçeneği bu davranışı değiştirir; aynı derleme tanıyı verir ve `dist`
dizini hiç oluşmaz.

Hangisinin isteneceği bağlama bağlıdır. Geliştirme sırasında, tip hatası olan bir kodu
yine de çalıştırabilmek yararlıdır — hata bir modülde, denenen davranış başka bir
modüldedir. Yayın hattında ise tersi geçerlidir: denetimden geçmeyen kod paketlenmemelidir.
Bu nedenle iki ayrı yapılandırma kullanmak yaygındır.

Sondaki `export {};` satırı ayrı bir ayrıntıdır: derleyici, dosyayı modül olarak işaretlemek
için ekler. Derleyici Yapılandırması dersindeki `moduleDetection` ayarı bu davranışı belirler.

#### İki Adıma Ayırma

Ayrım, iki komutla kurulur:

- **Tip denetimi:** `tsc --noEmit`. Çıktı üretmez, yalnızca tanıları verir.
- **Çıktı üretimi:** Dosya başına çalışan bir çevirici. Tipleri siler, sözdizimini hedefe
  uyarlar, tip denetimi yapmaz.

İkinci adımın hızlı olmasının nedeni, **dosya başına** çalışabilmesidir: bir dosyayı
çevirmek için başka dosyalara bakmaya gerek yoktur. Ancak bu, TypeScript'in bazı
yazımlarında olanaksızdır — çevirinin doğru olması için tip bilgisi gerekir.

`isolatedModules` seçeneği, bu yazımları yakalar. Proje kökünde şu `package.json` bulunsun:

```json
// package.json
{
  "name": "olcum-akisi",
  "type": "module",
  "version": "1.0.0"
}
```

Yapılandırma da şu olsun:

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "isolatedModules": true,
    "noEmit": true
  },
  "include": ["src"]
}
```

Dosyalar şunlardır. `src/olcum.ts`:

```typescript
// src/olcum.ts
export interface Olcum {
  kimlik: string;
  deger: number;
}

export function ortalama(kayitlar: readonly Olcum[]): number {
  return kayitlar.length === 0
    ? 0
    : kayitlar.reduce((t, k) => t + k.deger, 0) / kayitlar.length;
}
```

ve `src/ana.ts`:

```typescript
// src/ana.ts
import { Olcum, ortalama } from "./olcum.js";

export { Olcum };

const kayitlar: Olcum[] = [{ kimlik: "s-01", deger: 21.4 }];
console.log(ortalama(kayitlar));
```

`isolatedModules` açıkken:

```text
src/ana.ts(4,10): error TS1205: Re-exporting a type when 'isolatedModules' is enabled requires using 'export type'.
```

Sorun şudur: dosya başına çalışan bir çevirici, `Olcum` adının bir tip mi yoksa bir değer
mi olduğunu bilemez. Tipse dışa aktarma satırı tümüyle silinmeli, değerse korunmalıdır.
Karar için `olcum.ts` dosyasına bakmak gerekir ve bu, dosya başına çeviriyi bozar.

`verbatimModuleSyntax` seçeneği aynı kuralı içe aktarmalara da uygular. Yapılandırma şuna
dönüşür:

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "verbatimModuleSyntax": true,
    "noEmit": true
  },
  "include": ["src"]
}
```

Aynı `src/ana.ts` bu yapılandırmayla yeniden denetlenir:

```typescript
// src/ana.ts
import { Olcum, ortalama } from "./olcum.js";

export { Olcum };

const kayitlar: Olcum[] = [{ kimlik: "s-01", deger: 21.4 }];
console.log(ortalama(kayitlar));
```

```text
src/ana.ts(2,10): error TS1484: 'Olcum' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.
src/ana.ts(4,10): error TS1205: Re-exporting a type when 'verbatimModuleSyntax' is enabled requires using 'export type'.
```

Düzeltme, tip-yalnızca yazımları kullanmaktır:

```typescript
// src/ana.ts
import type { Olcum } from "./olcum.js";
import { ortalama } from "./olcum.js";

export type { Olcum };

const kayitlar: Olcum[] = [{ kimlik: "s-01", deger: 21.4 }];
console.log(ortalama(kayitlar));
```

Bu dosya `tsc` ile hatasız denetlenir. Blokların ilk satırındaki `// …` yorumu yalnızca dosyanın
adını gösterir, gerçek dosyanın parçası değildir. Yapılandırmadan `noEmit` kaldırılıp
`outDir: "dist"` ve `rootDir: "src"` eklendiğinde üretilen `dist/ana.js`:

```javascript
// dist/ana.js
import { ortalama } from "./olcum.js";
const kayitlar = [{ kimlik: "s-01", deger: 21.4 }];
console.log(ortalama(kayitlar));
```

`node dist/ana.js` çıktısı `21.4`tür.

`import type` yazımı, satırın tümüyle silineceğini **bildirimde** söyler. Kazanç yalnızca
hız değildir: içe aktarmanın yan etkisi olan bir modülden yalnızca tip almak, çıktıda o
modülün yüklenmesine yol açmaz. Modüller, Araçlar ve Ekosistem kursundaki ağaç sarsma
konusuyla aynı ilkedir — silinebilecek bağımlılığın silinebilir olduğunun bilinmesi.

#### Artımlı Derleme

`incremental` seçeneği, derleyicinin önceki çalışmasından kalan bilgiyi bir dosyaya
yazmasını sağlar. Sonraki derlemede yalnızca değişen dosyalar ve onlara bağlı olanlar
yeniden çözümlenir.

Seçenek açıkken proje kökünde `tsconfig.tsbuildinfo` adlı bir dosya oluşur. Bu dosya
üretilen bir yapıdır; sürüm kontrolüne alınmaz ve temizlik adımlarında silinir. Yeri
`tsBuildInfoFile` ile değiştirilebilir.

İzleme kipi (`tsc --watch`) aynı düşünceyi sürekli çalışan bir sürece taşır: dosya
değişiklikleri izlenir ve yalnızca etkilenen bölüm yeniden denetlenir. Geliştirme
sırasında bu kip, tek seferlik derlemeye yeğlenir.

#### Tip Soyan Çalışma Zamanları

Bazı çalışma zamanları TypeScript dosyalarını doğrudan çalıştırabilir: tip açıklamalarını
söker ve kalan JavaScript'i yürütür. Bu, ilk dersteki tip silme ilkesinin çalışma zamanına
taşınmış hâlidir.

Ayrımı görmek gerekir. Aşağıdaki `src/ana.ts` dosyası bir tip hatası içerir:

```typescript
// src/ana.ts
const deger: number = "21.4";
console.log(deger);
```

```text
src/ana.ts(2,7): error TS2322: Type 'string' is not assignable to type 'number'.
```

Derleyici bu tanıyı verir. Böyle bir çalışma zamanında ise dosya çalışır ve `21.4` yazar.
**Hiçbir tip denetimi yapılmaz.** Soyma işlemi mekaniktir; derleyicinin verdiği güvenceyle
ilgisi yoktur.

İkinci sınır, kod üreten TypeScript yazımlarında görülür. `src/birim.ts` dosyası:

```typescript
// src/birim.ts
enum Birim {
  Celsius = "C",
}
console.log(Birim.Celsius);
```

Bu dosya çalışmaz: çalışma zamanı `ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX` koduyla, tip soyma kipinde
`enum` bildiriminin desteklenmediğini bildirir.

Temel Tipler dersinde numaralandırmanın tip silme ilkesinin istisnası olduğu
gösterilmişti; sonucu burada somutlaşır. Yalnızca silme yapan bir çalışma zamanı,
silinemeyen sözdizimi çalıştıramaz. Aynı kısıt parametre özelliklerini de kapsar.

Bu nedenle `erasableSyntaxOnly` seçeneği, tip soyan bir çalışma zamanı hedefleyen
projelerde açık tutulur: silinemeyen yazımlar derleme anında bildirilir, çalışma zamanında
sürprize dönüşmez.

Üç yolun karşılaştırması:

| Yol | Tip denetimi | Hız | Kısıt |
|---|---|---|---|
| Derleyiciyle derleme | Var | Düşük | — |
| Ayrı denetim + dosya başına çeviri | Ayrı adımda | Yüksek | `isolatedModules` gerekir |
| Tip soyan çalışma zamanı | Yok | Yüksek | Silinemeyen yazım çalışmaz |

Üçünün ortak yanı, tip denetiminin **kaybolmaması** gerektiğidir. İkinci ve üçüncü yolda
denetim bir yerde — yayın hattında, düzenleyicide, işleme öncesi bir kancada — yapılmalıdır.
Yapılmadığında geriye kalan, tip açıklaması yazılmış JavaScript'tir.

#### Özet

- Tip hatası kendiliğinden yayını durdurmaz; `noEmitOnError` bunu değiştirir ve geliştirme
  ile yayın için ayrı yapılandırmalar yaygındır.
- Tip denetimi (`tsc --noEmit`) ile çıktı üretimi ayrı adımlara bölünebilir; ikinci adımın
  hızı dosya başına çalışabilmesinden gelir.
- `isolatedModules` ve `verbatimModuleSyntax`, dosya başına çeviriyi bozan yazımları
  bildirir; düzeltme `import type` ve `export type` yazımlarıdır.
- `incremental` seçeneği derleme bilgisini bir dosyaya yazar; bu dosya üretilmiş bir
  yapıdır ve sürüm kontrolüne alınmaz.
- Tip soyan çalışma zamanları hiçbir tip denetimi yapmaz ve silinemeyen yazımları
  çalıştıramaz; `erasableSyntaxOnly` bu yazımları derleme anında yakalar.

#### Kurs Kapanışı

Kurs, tek bir soruyla başladı: bir program çalıştırılmadan hakkında ne söylenebilir?
TypeScript'in yanıtı, kaynak kod üzerine kurulan ve çeviri bittiğinde geride kalmayan bir
kanıt katmanıydı.

Bu katmanın sınırları baştan çizildi ve her konuda yeniden sınandı. Tipler derleme anında
silinir; çalışma zamanı davranışını değiştirmez; dış dünyanın biçimini kanıtlamaz. Bu
kısıtlar bir eksiklik değil, katmanın ne olduğunun tanımıdır — ve `paths` ayarının
çalışmayan çıktısı ile tip soyan çalışma zamanının denetimsizliği, aynı kısıtın iki farklı
görünümüdür.

Kurs boyunca tek bir model taşındı. Ölçüm kaydı, hiçbir güvence taşımayan bir JavaScript
nesnesinden başladı; alanları tiplendi, birimi kapalı bir kümeye bağlandı, geçerli ve
hatalı durumları ayırt edici bir birleşimle ayrıldı, kaynağı bir arayüzle soyutlandı,
defteri bir tip parametresiyle genelleştirildi, türevleri eşlenmiş ve koşullu tiplerle
hesaplandı, sınırı bir tip yüklemiyle korundu ve son olarak bir paket bildirimiyle dışa
verildi. Her adımda kazanılan şey aynıydı: çalışma zamanında ortaya çıkacak bir sorunun
derleme anına taşınması.

Kurstan çıkarılacak üç ilke:

**Kanıt sınırda üretilir.** Sistem sınırından gelen veri `unknown` olarak alınır, çalışma
zamanında sınanır ve sonuç tip yüklemiyle kaydedilir. `as` yazımı bir kanıt değil bir
borçtur.

**Model tek yerde durur.** Türev tipler elle yazılmaz, asıl tipten hesaplanır; sabit
listeler hem değer hem tip olarak tek kaynaktan gelir. Modelin büyümesi, onu kullanan kodun
gözden geçirilmesini derleyici aracılığıyla zorunlu kılar.

**Katılık bir tercih değil, bir kapsam kararıdır.** Her bayrak bir hata sınıfına
karşılık gelir; kapalı bırakılan bayrak, o sınıfın çalışma zamanına bırakılması demektir.

Sırada, bu kodun çalışacağı yer var. **Node.js Çalışma Zamanı** kursu sunucu tarafındaki
çalışma zamanını ele alacak: olay döngüsünün aşamaları, dosya sistemi ve ağ arayüzleri,
akışlar ve geri basınç, süreç yönetimi ve üretim koşullarında günlükleme ile izleme. Bu
kursta kurulan tip katmanı orada da geçerlidir — çalışma zamanının sunduğu arayüzler
bildirim dosyalarıyla tiplenmiştir ve modül çözümleme kuralları o çalışma zamanının
kurallarıdır.

