İçeriğe geç
Educora
İleri18 dk7 / 8

Jenerikler ve yardımcı tipler

Her tiple çalışan ama tip bilgisini kaybetmeyen fonksiyonlar ve arayüzler yaz; onları `extends` ve `keyof` ile kısıtla, `Partial`, `Pick`, `Omit`, `Record` ve `ReturnType` ile yeni tipler üret.

Kendini test et
Bu derste öğreneceklerin
  • <T> tip parametreli jenerik bir fonksiyon yazmak
  • Jenerik arayüzler ve tipler tanımlamak
  • Tip parametrelerini extends ve keyof ile kısıtlamak
  • Partial, Pick, Omit, Record ve ReturnType yardımcı tiplerini uygulamak

Bir dizinin ilk elemanını döndüren bir fonksiyona ihtiyacın var: şehirler için, fiyatlar için, kullanıcılar için. Her tip için ayrı firstString, firstNumber, firstUser yazmak anlamsızdır. any ise tip bilgisini yok eder. Aşağıdaki kod hatasız derlenir ama çalışınca TypeError: city.toFixed is not a function hatasıyla çöker; derleyici artık city'nin bir string olduğunu bilmez:

TypeScript
function firstAny(items: any[]): any {
  return items[0];
}

const city = firstAny(['Baku', 'Ganja']);
console.log(city.toFixed(2));

Jenerik fonksiyonlar

Tanım
Jenerik (genel tip)

Bir tipi parametre olarak alan fonksiyon, arayüz ya da sınıf. Tip parametresi açılı parantez içinde yazılır (genellikle <T>) ve her kullanımda somut bir tiple değiştirilir; tıpkı sıradan bir parametrenin somut bir değerle değiştirilmesi gibi.

TypeScript
function first<T>(items: T[]): T | undefined {
  return items[0];
}

const city = first(['Baku', 'Ganja']);
const score = first([90, 75]);
const empty = first<boolean>([]);

console.log(city?.toUpperCase(), score?.toFixed(1), empty);
Beklenen çıktı
BAKU 90.0 undefined
city'nin tipi string | undefined, score'un tipi number | undefined'dır. Dizi boş olabileceği için undefined de hesaba katılmıştır.

Burada T, “henüz bilinmeyen bir tip” demektir. first(['Baku', 'Ganja']) çağrısında TypeScript argümana bakar ve T'nin string olduğunu kendisi çıkarır. Tip açıkça da verilebilir: first<boolean>([]). Bu, boş bir dizide olduğu gibi TypeScript'in tipi kendisi bulamadığı durumlarda gerekir.

Jenerik arayüzler

Bir tipin kendisi de parametre alabilir. Sunucu her zaman aynı yapıda yanıt verir (bir durum kodu ve veri) ama veri her istekte farklıdır. Her durum için ayrı bir arayüz yazmak yerine tek bir jenerik arayüz yazarız. Aslında jenerikleri zaten kullandın: Array<string> ve Promise<number> da jenerik tiplerdir.

TypeScript
interface ApiResponse<T> {
  status: number;
  data: T;
}

type User = { id: number; name: string };

const userResponse: ApiResponse<User> = {
  status: 200,
  data: { id: 1, name: 'Leyla' },
};

const tagsResponse: ApiResponse<string[]> = {
  status: 200,
  data: ['typescript', 'generics'],
};

console.log(userResponse.data.name, tagsResponse.data.length);
Beklenen çıktı
Leyla 2

Kısıtlar: extends ve keyof

Bazen T “herhangi bir tip” olamaz. İki değerden uzun olanı döndüren bir fonksiyonun length özelliğine ihtiyacı vardır. T extends { length: number } yazımı bir kısıttır: T yalnızca length özelliği olan bir tip olabilir; string, dizi vb. Sayıların ise length özelliği yoktur:

TypeScript
function longest<T extends { length: number }>(a: T, b: T): T {
  return a.length >= b.length ? a : b;
}

console.log(longest('Baku', 'Sumgait'));
console.log(longest([1, 2, 3], [4, 5]));
longest(10, 20);
Text
longest.ts:7:9 - error TS2345: Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.

7 longest(10, 20);
          ~~
Hatalı satırı silersen program Sumgait ve [ 1, 2, 3 ] yazdırır.

keyof T operatörü bir nesne tipinin tüm anahtarlarının birleşimini verir: { title: string; year: number } için bu 'title' | 'year''dır. T[K] ise o anahtardaki değerin tipidir. İkisini birleştirince yalnızca var olan özellikleri kabul eden ve tam doğru tipi döndüren bir fonksiyon elde edilir:

TypeScript
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const book = { title: 'Ali and Nino', year: 1937 };

const title = getProperty(book, 'title');
const year = getProperty(book, 'year');
console.log(title.toUpperCase(), year + 1);
getProperty(book, 'author');
Text
book.ts:10:19 - error TS2345: Argument of type '"author"' is not assignable to parameter of type '"title" | "year"'.

10 getProperty(book, 'author');
                     ~~~~~~~~
title, string; year ise number tipini alır. Var olmayan 'author' anahtarı reddedilir.

Yardımcı tipler

TypeScript'te jenerikler üzerine kurulmuş hazır yardımcı tipler (utility types) vardır. Var olan bir tipten yenisini üretirler; böylece aynı özellikleri iki kez yazmak zorunda kalmazsın. En sık kullanılanlar şunlardır:

TipNe yapar
Partial<T>tüm özellikleri isteğe bağlı yapar
Required<T>tüm özellikleri zorunlu yapar
Readonly<T>tüm özellikleri salt okunur yapar
Pick<T, K>yalnızca seçilen K özelliklerini tutar
Omit<T, K>K özellikleri dışında her şeyi tutar
Record<K, V>anahtarları K, değerleri V tipinde bir nesne
ReturnType<F>F fonksiyonunun döndürdüğü tip
TypeScript
interface Product {
  id: number;
  name: string;
  price: number;
  stock: number;
}

const laptop: Product = { id: 1, name: 'Laptop', price: 1500, stock: 4 };

function update(product: Product, changes: Partial<Product>): Product {
  return { ...product, ...changes };
}

type ProductCard = Pick<Product, 'name' | 'price'>;
type NewProduct = Omit<Product, 'id'>;

const card: ProductCard = { name: laptop.name, price: laptop.price };
const draft: NewProduct = { name: 'Mouse', price: 25, stock: 30 };

console.log(update(laptop, { price: 1350 }));
console.log(card, draft.name);
Beklenen çıktı
{ id: 1, name: 'Laptop', price: 1350, stock: 4 }
{ name: 'Laptop', price: 1500 } Mouse
Partial güncelleme için (yalnızca değişen özellikler), Omit<Product, 'id'> yeni ürün için (id'yi veritabanı verir), Pick ise küçük bir kart için.
TypeScript
type City = 'Baku' | 'Ganja' | 'Shaki';

const deliveryDays: Record<City, number> = {
  Baku: 1,
  Ganja: 2,
  Shaki: 3,
};

function createUser(name: string, age: number) {
  return { name, age, createdAt: '2025-09-01' };
}

type User = ReturnType<typeof createUser>;

const user: User = createUser('Elvin', 16);
console.log(deliveryDays.Ganja, user.name, user.createdAt);
Beklenen çıktı
2 Elvin 2025-09-01
Record<City, number> her şehir için bir değer ister: Shaki'yi unutursan TS2741 hatası alırsın.

Önemli noktalar

  • Bir jenerik, tipi parametre olarak alır: function first<T>(items: T[]): T | undefined.
  • T genellikle argümandan çıkarılır; gerekirse açıkça yazılır: first<boolean>([]).
  • T extends X tip parametresini kısıtlar; K extends keyof T yalnızca var olan anahtarlara izin verir.
  • Partial, Pick, Omit, Record ve ReturnType var olan tiplerden yenilerini üretir.
  • Bir tip parametresi en az iki yeri birbirine bağlamıyorsa jeneriğe gerek yoktur.

Kendini test et

10 soru. Her doğru cevap XP kazandırır.

1 / 10
function first<T>(items: T[]): T | undefined yazımında <T> nedir?