Menu

TypeScript Koşullu Tipler (Conditional Types) ve infer, Örneklerle

Koşullu tip, bir teste göre iki tipten birini seçer: T extends U ? X : Y. Sözdizimini, koşullu tiplerin union'lar üzerinde nasıl dağıldığını (ve bunun nasıl durdurulacağını), infer ile tip çıkarmayı ve ReturnType'ı kendiniz nasıl yazacağınızı öğrenin.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Koşullu tip, JavaScript'teki üçlü operatör gibi okunan bir testle iki tip arasında seçim yapar: T extends U ? X : Y. T, U tipine atanabiliyorsa sonuç X, aksi halde Y olur.

Buradaki extends "atanabilir" anlamına gelir; bu, bir değişkene değer atadığınızda derleyicinin kullandığı ilişkinin aynısıdır. Koşullu tipler yalnızca derleme zamanında vardır; JavaScript çıktısından silinirler.

Sözdizimi

type Result = CheckedType extends TestType ? TrueType : FalseType;

Koşullu tipler, kontrol edilen tipin daha sonra somut bir tip alan bir tip parametresi olduğu generics ile işe yarar hale gelir. true dalında TypeScript kontrol edilen tipin teste uyduğunu bilir; bu yüzden düz T tipinde message özelliği olmasa bile yukarıdaki T["message"] ifadesine izin verilir.

Zincirlenmiş üçlü operatörler gibi iç içe de yazılabilirler:

Fonksiyon gövdesindeki as TypeName<T> isteğe bağlı değildir; bir sonraki bölüm bunu gösteriyor.

Koşullu Dönüş Tipleri Bir Assertion Gerektirir

Dönüş tipi kendi tip parametresi üzerinde bir koşullu tip olan bir fonksiyon, dallardan hiçbirini doğrudan döndüremez. TypeScript gövde içinde T tipini daraltmaz, bu yüzden hangi dalın geçerli olduğunu söyleyemez:

index.ts(5,34): error TS2322: Type 'number' is not assignable to type 'Flip<T>'.
index.ts(5,45): error TS2322: Type 'string' is not assignable to type 'Flip<T>'.

İki yaygın çözüm var: her girdi çıktı çiftini belirten ve çağıranları kesin olarak kontrol eden overload'lar ya da implementasyonda bir assertion.

Overload'lar fonksiyon overload sayfasında anlatılıyor. Assertion ile derleyici size güvenir: gövdedeki yanlış bir dal yakalanmaz.

Dağılan Koşullu Tipler

Kontrol edilen tip çıplak bir tip parametresi olduğunda ve bir union aldığında, koşul her üye için bir kez çalışır ve sonuçlar yeni bir union'da birleştirilir:

Exclude ve Extract tiplerini çalıştıran şey bu dağılımdır. Exclude<T, U>, T extends U ? never : T olarak tanımlanır: U ile eşleşen her üye never olur ve never bir union'dan kaybolur. Bu yüzden Exclude<"a" | "b" | "c", "a">, "b" | "c" olur.

Aynı kuraldan iki sürpriz çıkar. boolean, true | false union'ıdır; bu yüzden ToArray<boolean>, boolean[] değil false[] | true[] olur. Ve never boş union'dır; dolayısıyla never verilen dağılan bir koşullu tip hiçbir şeyi test etmeden never döndürür:

type IsNever<T> = T extends never ? true : false;
type X = IsNever<never>; // never, not true

type IsNeverFixed<T> = [T] extends [never] ? true : false;
type Y = IsNeverFixed<never>; // true

infer ile Tip Çıkarmak

infer, extends ifadesinin içinde yeni bir tip değişkeni bildirir. Eşleşme başarılı olursa TypeScript bu değişkeni kontrol edilen tipten doldurur ve onu true dalında kullanabilirsiniz:

T extends Promise<infer V> ? V : T ifadesini şöyle okuyun: "T bir şeyin promise'i ise, o şeye V de ve onu döndür; değilse T tipini olduğu gibi döndür". infer yalnızca bir koşullu tipin extends ifadesinde kullanılabilir.

Bir infer değişkeni extends ile kendi kısıtlamasını taşıyabilir. O zaman eşleşme yalnızca çıkarılan tip uyarsa başarılı olur:

type FirstString<T> = T extends [infer S extends string, ...unknown[]] ? S : never;

type A = FirstString<["a", 1]>; // "a"
type B = FirstString<[1, "a"]>; // never: the first element is not a string

ReturnType'ı Kendiniz Yazmak

Yerleşik ReturnType, infer içeren tek satırlık bir koşullu tiptir. Onu kendiniz yazmak, iki fikri de oturtan klasik alıştırmadır:

typeof makeUser fonksiyon değerini tipine çevirir, ardından koşullu tip onu "herhangi bir fonksiyon" ile eşleştirir ve dönüş tipini R olarak yakalar. Standart kütüphanedeki sürüm iki ayrıntıda farklıdır: parametresi fonksiyon tipleriyle kısıtlanmıştır (T extends (...args: any) => any), bu yüzden ReturnType<string> never değil bir derleme hatası olur; false dalı ise any olur. ReturnType sayfası Parameters, InstanceType ve Awaited tiplerini de anlatıyor.

Özyinelemeli Koşullu Tipler

Bir koşullu tip kendisine başvurabilir; bu da her derinlikteki iç içe yapıyı açmasını sağlar:

type Flatten<T> = T extends readonly (infer U)[] ? Flatten<U> : T;

type A = Flatten<number[][][]>; // number
type B = Flatten<string>;       // string

Yerleşik Awaited<T> bu şekilde çalışır ve Promise<Promise<T>> tipini T tipine kadar açar. Pratikte özyinelemeyi sığ tutun: çok derin ya da sınırsız özyineleme derleyicinin error TS2589: Type instantiation is excessively deep and possibly infinite ile vazgeçmesine neden olur.

Hızlı Başvuru

KalıpAnlamı
T extends U ? X : YT, U tipine atanabiliyorsa X, değilse Y
T extends U ? never : TU ile eşleşen üyeleri çıkarır (bu Exclude tipidir)
T extends U ? T : neverU ile eşleşen üyeleri tutar (bu Extract tipidir)
[T] extends [U] ? X : Yaynı test, bir union üzerinde dağılmadan
T extends (infer E)[] ? E : Tbir dizinin eleman tipi
T extends Promise<infer V> ? V : Tbir promise'in değer tipi
T extends (...args: any[]) => infer R ? R : neverbir fonksiyonun dönüş tipi
T extends [infer H, ...infer Rest] ? ...bir tuple'ın ilk elemanı ve geri kalanı

Sıkça Sorulan Sorular

TypeScript'te koşullu tip nedir?

T extends U ? X : Y biçimindeki bir tiptir. T, U tipine atanabiliyorsa sonuç X, değilse Y olur. Tipler için derleme zamanında değerlendirilen bir if/else'tir; üretilen JavaScript'te ondan hiçbir şey kalmaz.

TypeScript'te infer anahtar kelimesi ne işe yarar?

infer, bir koşullu tipin extends ifadesi içinde bir tip değişkeni bildirir ve TypeScript'in onu eşleşen tipten doldurmasını sağlar. T extends Promise<infer V> ? V : T, bir promise tipinden çözümlenen değerin tipini çıkarır. Yalnızca bir koşullu tipin extends ifadesinde kullanılabilir.

Dağılan (distributive) koşullu tip nedir?

Kontrol edilen tip çıplak bir tip parametresi olduğunda ve ona bir union geçirdiğinizde, koşul her üyeye ayrı ayrı uygulanır ve sonuçlar birleştirilir. type ToArray<T> = T extends unknown ? T[] : never ile ToArray<string | number>, string[] | number[] olur. Bunu kapatmak için iki tarafı da köşeli parantez içine alın: [T] extends [unknown].

TypeScript'te bir fonksiyonun dönüş tipini nasıl alırım?

Yerleşik ReturnType<typeof fn> tipini kullanın. infer içeren bir koşullu tiptir: T extends (...args: any) => infer R ? R : any. Async fonksiyonlarda promise yerine çözümlenen değeri almak için onu Awaited<...> ile sarın.

IsNever<never> neden true yerine never döndürür?

never boş union'dır ve dağılan bir koşullu tip bir union'ın üyeleri üzerinde eşleme yapar. Hiç üye yoksa eşlenecek bir şey de yoktur ve sonuç never olur. never tipinin kendisini test etmek için [T] extends [never] ? true : false yazın.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA