Tuple, kaç öğe içerdiğini ve hangi konumda hangi tipin durduğunu bilen bir dizi tipidir: [string, number] ilk öğesi metin, ikinci öğesi sayı olan iki elemanlı diziyi anlatır. Tuple öğeleri isteğe bağlı (?), geri kalan (rest, ...T[]) ve etiketli ([ad: string]) olabilir. readonly ön eki ve ReadonlyArray<T> tipi ise dizinin tip denetimi sırasında değiştirilmesini engeller.

Tuple ve readonly diziler yalnızca tip düzeyinde vardır; derlenen JavaScript'te sıradan diziye dönüşürler. Bu yazıdaki örnekler TypeScript 6.0 ile --strict --target es2022 seçenekleriyle derlendi, çıktılar Node.js 24 ile alındı. Dizilerin çalışma zamanı metotları için JavaScript dizi metotları yazısına bakabilirsiniz.

Temel tuple tipi

Temel tuple tipi
typescript
type AdYas = [string, number];

const kisi: AdYas = ["Ayşe", 31];
const [ad, yas] = kisi;
console.log(ad.toUpperCase(), yas + 1); // AYŞE 32

const fazla = kisi[2];
// TS2493: Tuple type 'AdYas' of length '2' has no element at index '2'.
const ters: AdYas = [31, "Ayşe"];
// TS2322: Type 'number' is not assignable to type 'string'.
// TS2322: Type 'string' is not assignable to type 'number'.

Handbook'a göre basit bir tuple, belirli indeksler için özellik tanımlayan ve length değeri sayısal literal olan bir Array sürümüne denktir. Yıkım (destructuring) sırasında değişkenlere istediğiniz adı verebilirsiniz; bu da her öğenin anlamının gelenekle belli olduğu API'lerde tuple'ı kullanışlı yapar. Handbook, her öğenin anlamı herkes için açık değilse açıklayıcı adlara sahip bir nesnenin daha iyi olabileceğini de not ediyor. Yıkımın kendisi için destructuring ve spread yazısına bakın.

İsteğe bağlı öğeler

Öğe tipinin arkasına ? koyarsanız o öğe isteğe bağlı olur. İsteğe bağlı öğeler yalnızca sonda yer alabilir ve length tipini de etkiler:

typescript
type Nokta = [number, number, number?];

function boyut(n: Nokta) {
  const [x, y, z] = n; // z: number | undefined
  return n.length;     // tip: 2 | 3
}
console.log(boyut([1, 2]), boyut([1, 2, 3])); // 2 3

Rest öğeleri: başta, ortada, sonda

Bir tuple, tipi dizi veya tuple olan bir rest öğesi içerebilir. TypeScript 4.2'den beri rest öğesi yalnızca sonda değil, başta ya da ortada da olabilir. Rest öğesi olan bir tuple'ın sabit uzunluğu yoktur:

typescript
type Basta = [string, ...number[]];
type Ortada = [string, ...boolean[], number];

const a: Basta = ["toplam", 1, 2, 3];
const b: Ortada = ["bayrak", true, false, 7];
const c: Ortada = ["bayrak", 7];
console.log(a.length, b.length, c.length); // 4 4 2

Etiketli tuple öğeleri

Etiketli tuple öğeleri

TypeScript 4.0 ile tuple öğelerine etiket verebiliyorsunuz. Etiketler tip denetimini değiştirmez; editör ipuçlarında ve parametre listelerinde okunabilirliği artırır. Rest ve isteğe bağlı öğe sözdizimi parametre listesindekiyle aynıdır, bu yüzden tuple'ı rest parametresi olarak kullanmak doğal hâle gelir:

typescript
type Istek = [url: string, zamanAsimi?: number, ...basliklar: string[]];

function istekAt(...args: Istek) {
  const [url, zamanAsimi = 5000, ...basliklar] = args;
  return `${url} ${zamanAsimi} ${basliklar.length}`;
}
console.log(istekAt("/api"));                         // /api 5000 0
console.log(istekAt("/api", 1000, "Accept", "X-Id")); // /api 1000 2

type Aralik = [baslangic: number, bitis: number];
// TypeScript 5.2'den beri etiketli ve etiketsiz öğeler karışabilir:
type Karisik = [ilk: string, number];

4.0 sürüm notlarında "bir öğe etiketliyse hepsi etiketli olmalı" kuralı yer alıyordu; 5.2 bu kısıtı kaldırdı. Eski bir derleyici kullanıyorsanız Karisik tanımı hata verir. Tuple'ın parametre listesine dönüştüğü bu kullanım, fonksiyon tipleriyle yakından ilgilidir; fonksiyon tipleri ve overload yazısına bakabilirsiniz.

Fonksiyonlardan birden fazla değer döndürmek de etiketli tuple'ın tipik bir kullanımıdır:

typescript
function minMax(dizi: readonly number[]): [min: number, max: number] {
  return [Math.min(...dizi), Math.max(...dizi)];
}
const [enKucuk, enBuyuk] = minMax([4, 9, 1]);
console.log(enKucuk, enBuyuk); // 1 9

Tuple'ı yayarak fonksiyon çağırmak

Tuple ile parametre listesi arasındaki bu bağ ters yönde de çalışır: tipi tuple olan bir değişkeni spread sözdizimiyle (...) bir fonksiyona argüman olarak verebilirsiniz. TypeScript her konumdaki tipi ilgili parametreyle karşılaştırır. Aynı değerler sıradan bir diziye konursa, dizinin uzunluğu ve konum tipleri bilinmediği için çağrı reddedilir:

typescript
function konum(ad: string, x: number, y: number) {
  return `${ad}: ${x},${y}`;
}
const arg: [string, number, number] = ["A", 3, 4];
console.log(konum(...arg)); // A: 3,4

const dizi = ["B", 1, 2]; // (string | number)[]
konum(...dizi);
// TS2556: A spread argument must either have a tuple type or be passed to a
// rest parameter.

Burada dizi değişkeninin tipi (string | number)[] olarak çıkarılır, çünkü TypeScript dizi değişmezlerini varsayılan olarak tuple değil, genel dizi olarak görür. Değişkene tuple tipi yazmak ya da sonuna as const eklemek çağrıyı geçerli kılar. Spread ve rest parametrelerinin JavaScript tarafı default ve rest parametreler yazısında anlatılıyor.

ReadonlyArray ve readonly T[]

ReadonlyArray<T>, değiştirilmemesi gereken dizileri tanımlar. push, pop, splice gibi diziyi değiştiren metotlar bu tipte yoktur. Kısa yazımı readonly T[] şeklindedir; ikisi aynı tipi ifade eder. Array'in aksine ReadonlyArray diye bir kurucu (constructor) yoktur, normal bir diziyi bu tipe atarsınız.

typescript
function topla(sayilar: readonly number[]) {
  let t = 0;
  for (const s of sayilar) t += s;
  sayilar.push(4);
  // TS2339: Property 'push' does not exist on type 'readonly number[]'.
  return t;
}

let salt: ReadonlyArray<string> = ["a"];
let yazilir: string[] = ["b"];
salt = yazilir;   // tamam
yazilir = salt;
// TS4104: The type 'readonly string[]' is 'readonly' and cannot be assigned
// to the mutable type 'string[]'.

Atama tek yönlüdür: değiştirilebilir dizi readonly tipe verilebilir, tersi verilemez. Bu yüzden diziyi değiştirmeyen fonksiyonların parametresini readonly T[] yapmak iyi bir alışkanlıktır; çağıran hem normal hem readonly dizi gönderebilir.

readonly tuple ve as const

Tuple tiplerinin de readonly sürümü vardır: readonly [string, number]. Dizi değişmezine as const eklediğinizde TypeScript, öğeleri literal tipte olan bir readonly tuple çıkarır. Bu tip, değiştirilebilir tuple bekleyen bir parametreye verilemez:

typescript
const nokta = [3, 4] as const; // readonly [3, 4]

function uzaklik([x, y]: [number, number]) {
  return Math.sqrt(x ** 2 + y ** 2);
}
uzaklik(nokta);
// TS2345: Argument of type 'readonly [3, 4]' is not assignable to parameter of
// type '[number, number]'.
//   The type 'readonly [3, 4]' is 'readonly' and cannot be assigned to the
//   mutable type '[number, number]'.

function uzaklik2([x, y]: readonly [number, number]) {
  return Math.sqrt(x ** 2 + y ** 2);
}
console.log(uzaklik2(nokta)); // 5
nokta[0] = 5;
// TS2540: Cannot assign to '0' because it is a read-only property.

Çözüm, parametreyi readonly yapmaktır. Handbook, tuple'ların çoğunlukla oluşturulup değiştirilmeden kullanıldığını, bu yüzden mümkün olduğunda readonly tuple yazmanın iyi bir varsayılan olduğunu belirtiyor. as const hakkında daha fazlası literal tipler ve as const yazısında.

readonly çalışma zamanında koruma sağlamaz

readonly derleme sonrası silinir. Bir tip dönüşümüyle (type assertion) denetimi aşarsanız dizi değişir. Gerçek değişmezlik istiyorsanız JavaScript'in Object.freeze metodunu kullanırsınız; TypeScript, dondurulmuş diziyi otomatik olarak readonly number[] tipinde görür:

typescript
const liste: readonly number[] = [1, 2, 3];
(liste as number[]).push(4); // tip denetimini atlatır
console.log(liste);          // [ 1, 2, 3, 4 ]

const donmus = Object.freeze([1, 2, 3]); // readonly number[]
try {
  (donmus as number[]).push(4);
} catch (e) {
  console.log((e as Error).constructor.name); // TypeError
}

Hangi tipi ne zaman seçmeli?

Hangi tipi ne zaman seçmeli? T[] veya Array, readonly T[] veya ReadonlyArray, [A, B], [A, B?]
TipUzunlukDeğiştirilebilir miUygun olduğu yer
T[] veya Array<T>SerbestEvetGenel listeler
readonly T[] veya ReadonlyArray<T>SerbestHayır (tip düzeyinde)Diziyi değiştirmeyen fonksiyon parametreleri
[A, B]SabitEvetBirden fazla dönüş değeri, kısa çiftler
[A, B?]1 veya 2EvetSon öğesi isteğe bağlı kayıtlar
[A, ...B[]]En az 1EvetRest parametreleri modellemek
readonly [A, B]SabitHayır (tip düzeyinde)as const sonuçları, sabit koordinatlar

Sık yapılan hatalar

  • Tuple bekleyen yere tip yazılmamış bir dizi değişmezi vermek. const d = ["A", 1] tuple değil (string | number)[] olarak çıkarılır; tipi açıkça yazın veya as const kullanın.
  • as const ile üretilen değeri değiştirilebilir tuple parametresine vermek (TS2345). Parametreyi readonly yapın.
  • İsteğe bağlı öğeyi ortaya koymaya çalışmak. İsteğe bağlı öğeler yalnızca sonda olabilir; ortadaki değişken uzunluk için rest öğesi kullanın.
  • readonly işaretinin diziyi çalışma zamanında koruduğunu sanmak. Koruma yalnızca tip denetimindedir; gerekiyorsa Object.freeze ekleyin.
  • Çok sayıda öğeyi konumla anlatmak. Beş altı öğeyi aşan tuple'larda adlandırılmış özellikli bir nesne genellikle daha okunur olur.

Sık Sorulan Sorular

Tuple ile nesne arasında nasıl seçim yapmalıyım?

Öğelerin anlamı konumdan açıkça anlaşılıyorsa ve sayısı azsa tuple pratiktir. Öğe sayısı arttıkça veya anlamı belirsizleştikçe adlandırılmış özelliklere sahip bir nesne kodu daha okunur kılar.

map() bir tuple üzerinde çağrılınca sonuç tuple olur mu?

Hayır. map, filter gibi metotlar normal dizi tipi döndürür; örneğin [70, 85, 90].map((n) => n / 10) sonucu number[] olur. Belirli uzunluk korunmaz.

Variadic tuple nedir?

TypeScript 4.0 ile gelen variadic tuple, [...T, ...U] gibi generic tuple yayılımlarına izin verir ve fonksiyon parametrelerini birleştiren tipleri yazmayı kolaylaştırır. Generic tiplerle birlikte kullanıldığı için generics ve TypeScript ileri seviye yazılarına bakabilirsiniz.

Sabit uzunluklu bir tuple'a push yapılabilir mi?

Değiştirilebilir tuple tipi Array metotlarını taşıdığı için evet, ve bu bilinen bir boşluktur. Aşağıdaki kod hatasız derlenir; uzunluğu korumak istiyorsanız readonly tuple kullanın:

typescript
const cift: [number, number] = [1, 2];
cift.push(3); // derleyici itiraz etmez
console.log(cift, cift.length); // [ 1, 2, 3 ] 3

const sabit: readonly [number, number] = [1, 2];
sabit.push(3);
// TS2339: Property 'push' does not exist on type 'readonly [number, number]'.

readonly dizi JSON.stringify ile farklı mı serileşir?

Hayır. Çalışma zamanında sıradan bir dizidir; serileştirme, kopyalama ve döngüler aynı şekilde çalışır.

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

İlgili içerikler: TypeScript öğrenme rehberi · Literal tipler ve as const · Fonksiyon tipleri · Index signature ve Record · Type assertion · JavaScript dizi metotları · TypeScript ileri seviye