İçeriğe geç
TypeScript

TypeScript infer Keyword: İleri Seviye Tip Çıkarımı

Tarık Tunç
TypeScript infer Keyword: İleri Seviye Tip Çıkarımı

TypeScript infer Keyword: İleri Seviye Tip Çıkarımı

infer anahtar kelimesi, conditional type'lar içinde tip çıkarımı yapmanızı sağlayan güçlü bir TypeScript özelliğidir. Bir tipin belirli bir yapıya uyup uymadığını kontrol ederken, aynı zamanda o yapının parçalarını çıkarmanıza olanak tanır. Bu yazıda infer keyword'ünü derinlemesine inceleyeceğiz.

Temel infer Kullanımı

// infer, conditional type'ın extends kısmında kullanılır
// T extends SomeType<infer U> ? U : Default

// Array element tipini çıkarma
type ElementType<T> = T extends (infer E)[] ? E : never;

type A = ElementType<string[]>;      // string
type B = ElementType<number[]>;      // number
type C = ElementType<(string | number)[]>; // string | number
type D = ElementType<string>;        // never (array değil)

// Promise içindeki tipi çıkarma
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;

type E = Awaited<Promise<string>>;           // string
type F = Awaited<Promise<Promise<number>>>; // number (recursive)
type G = Awaited<string>;                     // string (Promise değil)

// Fonksiyon dönüş tipini çıkarma
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

type H = MyReturnType<() => string>;           // string
type I = MyReturnType<(x: number) => boolean>; // boolean
type J = MyReturnType<typeof parseInt>;         // number

Fonksiyon Parametrelerini Çıkarma

// Tüm parametreleri tuple olarak çıkarma
type Parameters<T> = T extends (...args: infer P) => any ? P : never;

type P1 = Parameters<(a: string, b: number) => void>; // [string, number]

// İlk parametreyi çıkarma
type FirstParam<T> = T extends (first: infer F, ...rest: any[]) => any
  ? F
  : never;

type F1 = FirstParam<(name: string, age: number) => void>; // string

// Son parametreyi çıkarma
type LastParam<T extends any[]> = T extends [...any[], infer L] ? L : never;

type L1 = LastParam<[string, number, boolean]>; // boolean

// Constructor parametrelerini çıkarma
type ConstructorParams<T> = T extends new (...args: infer P) => any
  ? P
  : never;

class KullaniciServisi {
  constructor(private apiUrl: string, private timeout: number) {}
}

type KSParams = ConstructorParams<typeof KullaniciServisi>;
// [string, number]

// Instance tipini çıkarma
type InstanceOf<T> = T extends new (...args: any[]) => infer I ? I : never;

type KS = InstanceOf<typeof KullaniciServisi>; // KullaniciServisi
İpucu: infer keyword'ü yalnızca conditional type'ın extends kısmında kullanılabilir. Başka bir yerde kullanmaya çalışırsanız derleme hatası alırsınız.

String Pattern ile infer

// String parçalarını çıkarma
type Split<S extends string, D extends string> =
  S extends `${infer T}${D}${infer U}`
    ? [T, ...Split<U, D>]
    : [S];

type Parts = Split<"a-b-c", "-">; // ["a", "b", "c"]

// Route parametrelerini çıkarma
type RouteParams<T extends string> =
  T extends `${string}:${infer Param}/${infer Rest}`
    ? { [K in Param | keyof RouteParams<Rest>]: string }
    : T extends `${string}:${infer Param}`
    ? { [K in Param]: string }
    : {};

type P = RouteParams<"/users/:userId/posts/:postId">;
// { userId: string; postId: string }

// Dosya uzantısını çıkarma
type Extension<T extends string> =
  T extends `${string}.${infer Ext}` ? Ext : never;

type Ext1 = Extension<"index.ts">;       // "ts"
type Ext2 = Extension<"styles.module.css">; // "module.css" (son nokta)
type Ext3 = Extension<"README">;          // never

İleri Seviye infer Pattern'leri

// Deep unwrap
type DeepUnwrap<T> =
  T extends Promise<infer U> ? DeepUnwrap<U> :
  T extends Array<infer U> ? DeepUnwrap<U>[] :
  T extends object ? { [K in keyof T]: DeepUnwrap<T[K]> } :
  T;

type Wrapped = Promise<{
  items: Promise<string[]>;
  count: Promise<number>;
}>;

type Unwrapped = DeepUnwrap<Wrapped>;
// { items: string[]; count: number }

// Fonksiyon overload'lardan son imzayı çıkarma
type OverloadReturnType<T> =
  T extends { (...args: any[]): infer R; (...args: any[]): any }
    ? R
    : T extends (...args: any[]) => infer R
    ? R
    : never;

// Tuple'dan union'a dönüştürme
type TupleToUnion<T extends any[]> = T[number];

type U = TupleToUnion<[string, number, boolean]>;
// string | number | boolean

// Union'dan intersection'a dönüştürme (ileri seviye)
type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends
  (k: infer I) => void ? I : never;

type Inter = UnionToIntersection<{ a: 1 } | { b: 2 }>;
// { a: 1 } & { b: 2 }
Uyarı: infer ile recursive tipler oluştururken TypeScript'in recursion limitine dikkat edin. Çok derin recursive tipler "Type instantiation is excessively deep and possibly infinite" hatasına neden olur. Recursion derinliğini sınırlandırmak için counter pattern kullanılabilir.

Pratik Kullanım Senaryoları

// Event listener tiplerini çıkarma
type EventPayload<T> =
  T extends `on${infer Event}`
    ? Event extends Capitalize<infer E>
      ? E
      : never
    : never;

type E1 = EventPayload<"onClick">;     // "click"
type E2 = EventPayload<"onSubmit">;    // "submit"

// Redux action tiplerini çıkarma
type ActionCreator = {
  kullaniciEkle: (isim: string) => { type: "KULLANICI_EKLE"; payload: string };
  kullaniciSil: (id: number) => { type: "KULLANICI_SIL"; payload: number };
};

type ActionTypes<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => infer A ? A : never;
}[keyof T];

type AllActions = ActionTypes<ActionCreator>;
// { type: "KULLANICI_EKLE"; payload: string } | { type: "KULLANICI_SIL"; payload: number }
Bilgi: infer keyword'ü, TypeScript'in "pattern matching" yeteneğinin temelidir. Rust'taki pattern matching veya Haskell'deki pattern destructuring'e benzer şekilde, bir tipin yapısını analiz etmenizi ve parçalarını çıkarmanızı sağlar.

Sonuç

infer keyword'ü, TypeScript'in conditional type sisteminin en güçlü aracıdır. Fonksiyon parametreleri, dönüş tipleri, Promise içerikleri, string parçaları ve daha fazlasını çıkarmanızı sağlar. İleri seviye tip programlamasında infer'ı ustalaşmak, karmaşık tip dönüşümlerini ve tip güvenli API'ler oluşturmanızı mümkün kılar.

Kaynaklar

İlgili Yazılar