TypeScript'te Infer: Otomatik Tür Çıkarımı ile Güçlü Tip Tanımlamaları

Bu blog yazısında, TypeScript'te `infer` anahtar kelimesini bazı dikkat çekici örneklerle öğreneceğiz. Örneğin, türleri dönüştürme, özyinelemeli (recursive) türler gibi...
TypeScript'te Infer Nedir?
TypeScript'te `infer`, koşullu (conditional) türler içinde kullanılan ve bir türü başka bir tür üzerinden çıkarmaya (türetmeye) yarayan bir anahtar kelimedir. Kod içinde bir değişkenin veya parametrenin kullanımına göre türünü otomatik olarak belirlemeyi sağlar.
`infer` daima bir koşullu tür içinde, yani `extends` anahtar kelimesi ile birlikte kullanılmalıdır.
Bir Fonksiyonun Dönüş Türünü Bulmak
type ReturnFuncType<T> = (...args: any) => T extends infer R ? R : never
const foo = () => 2
type Foo = ReturnFuncType<typeof foo>
// type Foo = (...args: any) => () => number
`ReturnFuncType< T >`: Bu tür, bir fonksiyonu parametre olarak alır ve fonksiyonun dönüş türünü aşağıdaki koşul üzerinden çıkarmaya çalışır:
T extends `${infer R}${infer K}` ? `${ReverseType<K>}${R}` : T
Bu sayede, herhangi bir fonksiyonun dönüş türünü kolayca elde edebiliriz.
type ReverseType<T> =
T extends`${infer R}${infer K}` ? `${ReverseType<K>}${R}` : T
type test1 = ReverseType<'1234'> // test1:'1234'
type test2 = ReverseType<'typescript-guru'> // test2:'urug-tpircsepyt'
type test3 = ReverseType<'1_2_3'> // test3:'3_2_1'
`ReverseType< T >`: Bu tür, verilen string türünün ilk karakterini çıkarıp sona ekleyerek işlemi tekrarlar. Bu işlem, verilen türün sonuna ulaşana kadar devam eder:
T extends `${infer R}${infer K}` ? `${ReverseType<K>}${R}` : T
String İçindeki Sayıları Kaldırma
type numbers = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0'
type RemoveNumber<T> = T extends numbers ? never : T
type RemoveNumberFromString<T> =
T extends `${infer R}${infer K}` ?
R extends numbers ?
`${RemoveNumberFromString<K>}` : `${R}${RemoveNumberFromString<K>}`
: RemoveNumber<T>
type test1 = RemoveNumberFromString<'test'> // test1:'test'
type test2 = RemoveNumberFromString<'1234'> // test2:''
type test3 = RemoveNumberFromString<'123test123'> // test3:'test'
type test4 = RemoveNumberFromString<'1t2e3s4t'> // test4:'test'
`RemoveNumberFromString< T >`: Bu tür, string türünü karakter karakter kontrol eder. Eğer karakter bir sayı ise, bu karakter çıkarılır. İşlem aşağıdaki koşul üzerinden gerçekleştirilir:
T extends`${infer R}${infer K}` ?
R extends numbers ?
`${RemoveNumberFromString<K>}` : `${R}${RemoveNumberFromString<K>}`
: RemoveNumber<T>
Bu koşul karmaşık görünebilir ancak aslında diğer programlama dillerindeki özyinelemeli fonksiyonlara benzer şekilde çalışır. Buradaki en önemli nokta, base case yani durma koşuludur.
Kebab-case'i Snake_case'e Dönüştürme
type Kebab2Snake<T> =
T extends`${infer R}-${infer K}` ? `${R}_${Kebab2Snake<K>}` : T
type test1 = Kebab2Snake<'test-1'> // test1:'test_1'
type test2 = Kebab2Snake<'test-2'> // test2:'test_2'
type test3 = Kebab2Snake<'kebab-case'> // test3:'kebab_case'
`Kebab2Snake< T >`: Bu tür, '…-…' şeklindeki kebab-case desenini bulur ve bunu '…_…' biçiminde snake_case'e dönüştürür. Dönüşüm aşağıdaki koşulla yapılır:
T extends `${infer R}-${infer K}` ? `${R}_${Kebab2Snake<K>}` : T
Özet
`infer`, koşullu türlerle birlikte (`extends`) kullanılmalıdır.
Bilmediğimiz bir türü bulmak ve çıkarmak için `infer` kullanabiliriz.
`infer` kullanarak özyinelemeli (recursive) işlemler tanımlayabiliriz.
Geri