Menu

TypeScript Generic Kısıtlamaları: extends ve keyof T

Bir generic kısıtlaması (T extends Something), bir tip parametresinin hangi tipleri kabul ettiğini sınırlar ve fonksiyonun kısıtlamanın garanti ettiği şeyleri kullanmasına izin verir. Nesne biçimleri ve interface'lerle extends, güvenli özellik erişimi için K extends keyof T, primitive tiplerle kısıtlamalar ve karşılaşacağınız hatalar.

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

Bir generic kısıtlaması, bir tip parametresinin ne olabileceğini sınırlar. <T extends { length: number }> yalnızca sayısal bir length özelliğine sahip tipleri (string'ler, diziler, kendi nesneleriniz) kabul eder ve bu vaat sayesinde fonksiyonun .length okumasına izin verilir.

Dönüş tipi { length: number } değil, hâlâ T tipidir: iki sayı dizisi geçirirseniz geriye number[] döner ve nesneler tag özelliklerini korur. Kısıtlama asgariyi tarif eder; çağıranın tipi korunur.

Kısıtlamalara Neden İhtiyaç Var

Generic bir fonksiyonun içinde kısıtlanmamış bir T herhangi bir şey olabilir: bir string, bir sayı, null, bir fonksiyon. Bu yüzden TypeScript yalnızca her tipte çalışan şeylere izin verir, bu da neredeyse hiçbir şeydir.

Derleyici şunu yazdırır:

index.ts(3,12): error TS2339: Property 'length' does not exist on type 'T'.
index.ts(3,24): error TS2339: Property 'length' does not exist on type 'T'.

Çözüm neye ihtiyacınız olduğunu söylemektir: <T extends { length: number }>. Kısıtlamalar "herhangi bir tip" ifadesini "şuna sahip herhangi bir tip" ifadesine dönüştürür; generic bir fonksiyonun kastettiği de genellikle budur.

Bir Interface ile Kısıtlamak

Kısıtlama, zaten sahip olduğunuz bir interface ya da type alias dahil herhangi bir tip olabilir. Fonksiyon bu durumda en azından o üyelere sahip her şeyle çalışır ve yine de çağıranın tam tipini döndürür.

Düz bir parametre tipiyle karşılaştırın: function byId(items: HasId[], id: number): HasId | undefined aynı dizileri kabul eder, ama sonuç yalnızca HasId olur; bu yüzden found.name bir hata olurdu. Generic sürüm tam tipi olduğu gibi aktarır.

K extends keyof T: Güvenli Özellik Erişimi

En yaygın kısıtlama iki tip parametresini birbirine bağlar. K extends keyof T, K değerinin T tipinin özellik adlarından biri olması gerektiğini söyler; T[K] ise o özelliğin tipidir.

Yanlış yazılmış ya da var olmayan bir anahtar bir derleme hatasıdır ve dönüş tipi anahtarı izler: "year" sonucu number, "title" sonucu string olur. İndeks imzalarıyla nasıl davrandığı dahil keyof operatörünün kendisi keyof sayfasında anlatılıyor.

Primitive Tiplerle Kısıtlamak

Bir kısıtlama bir primitive tip ya da onların bir union'ı olabilir. İşe yarar bir yan etkisi var: T extends string (ya da number) olduğunda TypeScript argümanın tipini genişletmek yerine onun literal tipini çıkarır.

Kısıtlama olmadan createEvent("user.saved"), { type: string } döndürür: burada TypeScript T için çıkardığı literal'i string olarak genişletir. T extends string ile literal korunur; tipli event ve routing yardımcıları düz string argümanlarından tam adları bu şekilde elde eder.

Sık Görülen Hatalar

Yalnızca kısıtlamaya uyan bir şey döndürmek. T döndüren bir fonksiyon, kısıtlamaya uyan herhangi bir değeri değil, bir T döndürmelidir:

interface HasId {
  id: number;
}

function reset<T extends HasId>(item: T): T {
  return { id: item.id };
}
// error TS2322: Type '{ id: number; }' is not assignable to type 'T'.
//   '{ id: number; }' is assignable to the constraint of type 'T', but 'T' could be
//   instantiated with a different subtype of constraint 'HasId'.

T tipi { id: number; name: string } ise yeni nesnenin name özelliği yoktur, dolayısıyla bir T değildir. Her özelliği koruyan bir spread döndürün, return { ...item, id: 0 };, ya da ürettiğiniz tek şey buysa dönüş tipini HasId olarak bildirin.

Kısıtlamayı bozan bir tip argümanı geçirmek. Açık tip argümanları da kontrol edilir: function scale<T extends number>(x: T) ile scale<string>("2") çağrısı Type 'string' does not satisfy the constraint 'number'. (TS2344) hatasıyla başarısız olur.

Generic gerekmediği halde kısıtlama yazmak. T yalnızca parametrede geçiyorsa function print<T extends HasId>(item: T): void, fazladan sözdizimiyle yazılmış function print(item: HasId): void ifadesinden ibarettir. Kısıtlamalar, T dönüş tipinde ya da başka bir parametrede de geçtiğinde önem kazanır.

Varsayılanlı Kısıtlamalar

Bir tip parametresinin hem bir kısıtlaması hem de bir varsayılanı olabilir. Varsayılan, kısıtlamayı karşılamalıdır.

interface Store<TState extends object = Record<string, unknown>> {
  get(): TState;
  set(next: Partial<TState>): void;
}

type AnyStore = Store;                              // Store<Record<string, unknown>>
type CounterStore = Store<{ count: number }>;       // fine
type BadStore = Store<number>;
// error TS2344: Type 'number' does not satisfy the constraint 'object'.

Aynı extends anahtar kelimesi koşullu tiplerde de (T extends string ? A : B) görünür; orada bir sınırlama değil bir testtir. Bu biçim koşullu tipler sayfasında anlatılıyor.

Sıkça Sorulan Sorular

TypeScript generic'lerinde T extends ne anlama gelir?

<T extends Constraint>, T tipinin Constraint tipine atanabilen herhangi bir tip olabileceği anlamına gelir. Çağıranlar yalnızca bu tür tipleri geçirebilir ve fonksiyonun içinde kısıtlamanın garanti ettiği her şeyi kullanabilirsiniz. <T extends { length: number }>, string'leri, dizileri ve sayısal bir length özelliğine sahip her nesneyi kabul eder ve value.length okumanıza izin verir.

K extends keyof T ne anlama gelir?

K, T tipinin özellik adlarından biri olmalıdır. İndeksli erişim tipi T[K] ile birlikte özellik erişimini tam olarak tipler: function get<T, K extends keyof T>(obj: T, key: K): T[K] yalnızca obj nesnesinin gerçek anahtarlarını kabul eder ve dönüş tipi o özelliğin tipidir.

Neden "Property does not exist on type T" hatası alıyorum?

Kısıtlanmamış bir T, number veya null dahil herhangi bir tip olabilir, bu yüzden TypeScript onun üzerinde tipe özgü hiçbir şeye izin vermez (TS2339). Özelliği vaat eden bir kısıtlama ekleyin: <T extends { name: string }> ya da zaten sahip olduğunuz bir interface ile kısıtlayın: <T extends User>.

"T could be instantiated with a different subtype of constraint" ne anlama gelir?

TS2322 hatası: fonksiyon T döndürmeyi vaat ediyor ama yalnızca kısıtlamaya uyan bir değer döndürüyor. T tipi { id: number; name: string } ise yeni bir { id: 1 } bir T değildir. Aldığınız değeri (ya da buna göre tiplenmiş bir spread kopyasını) döndürün ya da dönüş tipini kısıtlamanın kendisi yapın.

Generic'teki extends ile sınıf veya interface'teki extends arasındaki fark nedir?

Aynı anahtar kelime, ilişkili bir fikir. class Dog extends Animal ve interface B extends A içinde kalıtım bildirir. <T extends A> içinde bir kısıtlama bildirir: T, A tipine atanabilir olmalıdır. Bir koşullu tipte, T extends A ? X : Y, iki tipten birini seçen bir testtir.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA