TypeScript'te generik argümanlarının yerleştirilmesi arasındaki fark
Genel fonksiyon sözdizimi arasındaki fark nedir:
type Identity<T> = (t: T) => T
ve
type Identity = <T>(t: T) => T
?
Cevap #1
TypeScript'te iki farklı türde generic vardır: generic fonksiyonlar ve generic tipler.
Genel bir fonksiyon, genel tür parametresini (veya parametrelerini) fonksiyonun çağrı imzasında bildirir:
type IdentityGenFunc = <T>(t: T) => T
Genel bir fonksiyondaki tip parametresi (T yukarıda) fonksiyon gerçekten çağrılana kadar belirtilmez; bu noktada çağıran kişi bunu belirtir (veya derleyici çağıran kişi adına bunu çıkarır). Bu, bir genel fonksiyon uygulamasının, fonksiyon çağıranın istediği T’nin herhangi bir olası belirtimiyle başa çıkabilmesi gerektiği anlamına gelir:
const idGenFunc: IdentityGenFunc = x => x;
const resultABC = idGenFunc("ABC"); // T inferred here as ABC
// const resultABC: "ABC"
const result123 = idGenFunc(123); // T inferred here as 123
// const result123: 123
Genel bir tip, genel tip parametresini (veya parametrelerini) tip bildirimi üzerinde tanımlar:
type IdentityGenType<T> = (t: T) => T
Genel bir tür üzerindeki tür parametresi (T yukarıda) o türde bir değere sahip olabilmeden önce belirtilmelidir. Eğer bir genel tür, tür parametresine atıfta bulunan bir çağrı imzasına veya metoda sahipse, o tür parametresi, çevreleyen genel tür belirtildiğinde sabitlenir, o metod çağrılmadan önce. Örneğin bir IdentityGenType<string>'ten söz ediyorsanız, çağrı imzasının yalnızca bir string ile başa çıkabilmesi gerekir. Bu yüzden bu fonksiyonu uygulamak için T'nin olası herhangi bir değeriyle ilgilenmenize gerek yoktur:
const idGenTypeString: IdentityGenType<string> = x => x + "!";
const resultABCagain = idGenTypeString("ABC"); // okay, "ABC" is assignable to string
// const resultABCagain: string
const result123Again = idGenTypeString(123); // error! 123 is not assignable to string
Bu iki jenerik tür birbiriyle ilişkilidir. IdentityGenFunc türü esasen her olası T için IdentityGenType<T> türünün bir "sonsuz kesişimi"dir. TypeScript tür sistemi bunu söyleyecek kadar ifade edici değildir:
// the following is not valid TS, but this is what you want to say
type IdentityGenFunc = forall T, IdentityGenType<T>; // not valid TS, error
ama bunu ödevlerle gözlemleyebilirsiniz:
// every IdGenFunc is also an IdGenType<number>
const idGenTypeNumber: IdentityGenType<number> = idGenFunc; // okay
Öte yandan, IdentityGenType<T> öğesini, bazı T türüyle örneklenmiş IdentityGenFunc ile aynı olarak düşünebilirsiniz. Yine, tür sistemi bunu ifade etmenize izin vermez:
// the following is not valid TS, but this is what you want to say
type GenType<T> = instantiate GenFunc with T; // not TS, error
TypeScript 4.7, türü bir jenerik fonksiyon olan bir değer üzerinde tür parametrelerini başlatmanıza izin verecek başlatma ifadelerini tanıtacak olmasına rağmen:
// if you instantiate a value of type IdGenFunc with Date, then you get an IdGenType<Date>
const idGenTypeDate: IdentityGenType<Date> = idGenFunc<Date>; // okay for TS4.7+
Koda yönelik oyun alanı bağlantısı
Cevap #2
Benim deneyimime göre:
type Type <T>=(t: T) => Tiçin, bu bir fonksiyon gibidir,T'yi mantıklı hale getirmek için bir parametre vermeniz gerekir.type Type=<T> (t: T) => Tiçin, bu sadece<T> (t: T) => Ttipine başka bir isim vermek anlamına gelir ve buradaT,tyerine verdiğiniz değere göre belirlenecek ve onu belirlemek için parametre veremezsiniz.
Genel bir fonksiyon için, örneğin const a=<T,> (t: T): T => t ,bir type a=<T> (t: T) => T tanımlayabilir ve sonra bunun için const a: a=<T,> (t: T): T => t yazabilirsiniz:
type a = <T> (t: T) => T
const a: a = <T,> (t: T): T => t
// same as const a: <T> (t: T) => T = <T,> (t: T): T => t
Tipi T, fonksiyonun kendisi tarafından belirlenir.
Eğer bunun T’nin sadece fonksiyonun kendisi değil de başka bir şeye karar vermesi gerekiyorsa, şöyle:
type aa <T> = { head: T, tail: (t: T) => T }
O zaman bir tür takma adı tanımladığınızda <T>, = işaretinin sol tarafında olmalıdır. Burada, T aynı zamanda tail tarafından, yani işlevin kendisi tarafından da belirlenebilir, ancak bu (t: T) => T türündeki işlevin parametresinin türü, head alanındaki değerin türüyle eşit olmalıdır. Nerede değiştirirseniz değiştirin, bunları eşit hale getirmelisiniz veya bu tür takma ad tanımı da değiştirilmelidir.
ör.
Şunları tanımlayabilirsiniz:
type Fn <T, R> = (x: T) => R ;
type Pipeyard <T> = <R,> (continuation: Fn<T, R>) => Pipeyard<R> ;
const Pipeyard =
<T,> (head: T)
: Pipeyard<T> =>
( <R,> (continuation: Fn<T, R>)
: Pipeyard<R> =>
Pipeyard (continuation(head))
) as Pipeyard <T> ;
Bunun = işaretinin sol tarafında <T> ve sağ tarafında <R> vardır.
Bu define şöyle kullanılabilir:
Pipeyard (3)
(x => `x: ${x}`)
(x => "~ " + x)
(console.log); // "~ x: 3"
Türlerin türetilmesi otomatik! Çünkü bir sonraki <T> olan <R>, verdiğiniz fonksiyonun dönüş türü tarafından belirlenir.
(Ve bu kodlar pure-symbols/pure.closures deposundan agpl-3.0 lisansı altında 🙃)
