TypeScript Conditional Types: Koşullu Tip Sistemi

TypeScript Conditional Types: Koşullu Tip Sistemi
Conditional type'lar, tip seviyesinde if/else mantığı kurmanızı sağlayan güçlü bir TypeScript özelliğidir. Bir tipin başka bir tipe atanabilirliğine göre farklı tipler döndürür. Bu yazıda conditional type'ları temellerden ileri seviye kullanımlara kadar kapsamlı örneklerle ele alacağız.
Temel Sözdizimi
// Temel conditional type
// T extends U ? X : Y
// "T, U'ya atanabilir mi? Evet ise X, hayır ise Y"
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
type C = IsString<"hello">; // true (literal tip de string)
// Pratik örnek: Null kontrolü
type NonNullable<T> = T extends null | undefined ? never : T;
type D = NonNullable<string | null>; // string
type E = NonNullable<number | undefined>; // number
type F = NonNullable<null>; // never
// İç içe conditional
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" :
"object";
type T1 = TypeName<string>; // "string"
type T2 = TypeName<() => void>; // "function"
type T3 = TypeName<string[]>; // "object"
Distributive Conditional Types
Union type bir conditional type'a verildiğinde, her üye ayrı ayrı değerlendirilir:
// Distributive davranış
type ToArray<T> = T extends any ? T[] : never;
type StrArr = ToArray<string>; // string[]
type NumArr = ToArray<number>; // number[]
type Union = ToArray<string | number>; // string[] | number[]
// Non-distributive yapmak için []
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type UnionArr = ToArrayNonDist<string | number>; // (string | number)[]
// Exclude implementasyonu (distributive)
type MyExclude<T, U> = T extends U ? never : T;
type Sonuc = MyExclude<"a" | "b" | "c", "a" | "c">;
// "a" extends "a"|"c" ? never : "a" → never
// "b" extends "a"|"c" ? never : "b" → "b"
// "c" extends "a"|"c" ? never : "c" → never
// Sonuç: "b"
// Extract implementasyonu
type MyExtract<T, U> = T extends U ? T : never;
type SadeceString = MyExtract<string | number | boolean, string | boolean>;
// string | boolean
[T] ile sarmalayın.
infer ile Tip Çıkarımı
// Fonksiyon dönüş tipini çıkarma
type ReturnTypeOf<T> = T extends (...args: any[]) => infer R ? R : never;
type A = ReturnTypeOf<() => string>; // string
type B = ReturnTypeOf<(x: number) => boolean>; // boolean
// Fonksiyon parametre tiplerini çıkarma
type ParamsOf<T> = T extends (...args: infer P) => any ? P : never;
type C = ParamsOf<(a: string, b: number) => void>; // [string, number]
// Promise içindeki tipi çıkarma
type UnwrapPromise<T> = T extends Promise<infer U> ? UnwrapPromise<U> : T;
type D = UnwrapPromise<Promise<string>>; // string
type E = UnwrapPromise<Promise<Promise<number>>>; // number
// Array element tipini çıkarma
type ElementOf<T> = T extends (infer E)[] ? E : T;
type F = ElementOf<string[]>; // string
type G = ElementOf<number>; // number (array değilse kendisi)
// Object value tiplerini çıkarma
type ValueOf<T> = T extends Record<any, infer V> ? V : never;
type H = ValueOf<{ a: string; b: number }>; // string | number
Pratik Kullanım Senaryoları
// API yanıt tipi belirleme
interface Endpoints {
"/kullanicilar": Kullanici[];
"/kullanicilar/:id": Kullanici;
"/urunler": Urun[];
"/urunler/:id": Urun;
}
type ApiYanit<T extends keyof Endpoints> = Endpoints[T];
async function apiIstegi<T extends keyof Endpoints>(
url: T
): Promise<ApiYanit<T>> {
const response = await fetch(url);
return response.json();
}
// Tip otomatik çıkarılır
const kullanicilar = await apiIstegi("/kullanicilar"); // Kullanici[]
const urun = await apiIstegi("/urunler/:id"); // Urun
// Koşullu prop tipleri
type InputProps<T extends "text" | "number" | "checkbox"> = {
type: T;
value: T extends "checkbox" ? boolean : T extends "number" ? number : string;
onChange: (value: T extends "checkbox" ? boolean : T extends "number" ? number : string) => void;
};
// Recursive conditional type
type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T;
type Nested = number[][][];
type Flat = Flatten<Nested>; // number
Sonuç
Conditional type'lar, TypeScript tip sisteminin en güçlü ve esnek özelliklerinden biridir. Temel koşullu ifadelerden başlayarak, distributive davranış ve infer keyword ile ileri seviye tip manipülasyonları yapabilirsiniz. Bu kavramları anlamak, TypeScript'te profesyonel seviyede tip tanımlamaları yapmanızın anahtarıdır.