typescript - Parametre olarak verilen genel fonksiyonun çağrısı sonucu olan bir fonksiyonun dönüş tipini nasıl tanımlarsınız
Soru, bar fonksiyonunun dönüş tipini nasıl tanımlayacağımızdır; bu tip, genellikle fn adlı generik fonksiyonun çalıştırılmasının sonucu olmalıdır.
function foo(fn<T>) /*return →*/ function bar<B> /*return →*/ call fn<B>()Örnek :
function foo<T extends <X>(...args: any) => any>(fn: T) {
return function bar<B>(): ReturnType<typeof fn> /**👈*/ {
return fn<B>({ /*config*/ });
}
}
{
const f1 = <T>() => Promise.resolve(({/*input*/ } as T));
const x1 = foo(f1)
const y1 = x1<{ a: 'A' }>()/**Promsise<unknown>❌*/.then(data => data.a); // should be Promise<{ a: 'A'}> ✅
const y12 = x1<{ b: 'B' }>()/**Promsise<unknown>❌*/.then(data => data.b); // should be Promise<{ b: 'B'}> ✅
}
{
const f2 = <T>() => ({/*input*/ } as T);
const x2 = foo(f2)
const y2 = x2<{ a: 'A' }>()/**unknown❌*/.value // should be { a: 'A'} ✅
const y21 = x2<{ b: 'B' }>()/**unknown❌*/.value // should be { b: 'B'} ✅
}
Teşekkürler 🙏
Güncelleme 1 🚧:
TypeScript ile ilgili sorunlar
- https://github.com/microsoft/TypeScript/issues/37181
- https://github.com/microsoft/TypeScript/issues/40542
Cevap #1
İlginç bir soru, çünkü bu alanda bir platform mühendisi olarak kodlama teknikleriyle çok deneme yaptım, bu yüzden bazı önerilerimi paylaşayım dedim.
Kendi başına generiklerin tam bir çözüm sunmadığını veya en okunabilir kodu sağlamadığını gördüm. API çağrılarıyla uğraşırken gereksinimleri formüle etmek ve farklı sorumluluklara sahip katmanlar sağlamak faydalı olabilir. İşte kendi uygulamalarımda kullandığım bir yaklaşım örneği - React + Axios ile ifade edilmiştir.
GEREKSİNİMLER
- React görünümleri, API yükleriyle uğraşırken tür güvenli bir deneyim elde etmelidir
- Güvenilirlik ve tekrar denemeler, birden fazla uygulamada kullanılan paylaşılan sınıflar tarafından ele alınmalıdır, örneğin API çağrılarını yeniden denemek, devre kesici uygulamak veya herhangi başka bir şey için
- Kod basit ve okunabilir olmalıdır
KOD PARÇACIKLARI
React görünüm modeli, tür dönüşümü yapmadan yazılmış API varlıklarını kullanır ve kullanıcı arayüzü bunu tür güvenli bir şekilde görüntüler. Tüm görünümler ve görünüm modelleri bu deneyimi elde eder.
Fetch sınıfı düşük seviyeli şeyleri uygular ve birden fazla uygulama arasında paylaşılabilir. Bir noktada burada jenerik kullandım ve kod çok daha az okunabilirdi.
Servis ajanı sınıfı. Görünümleri en doğal şekilde sunan yöntemlere sahip basit bir adaptör. Burada tip dönüşümleri var ama sadece bu katmanda bulunuyorlar.
Diğer yerlerde sınırlı generikler kullanılır, örneğin
axios.get<T>'i çağırmak için. Ancak, ayrı istek ve yanıt türleri ile birden çok yerde generik kullanmak hızla karışık hale gelir.
SONUÇ
Bazen katmanlama ve sorumlulukların ayrılması, jeneriklerin çok fazla kullanılmasından daha temiz bir çözüm sağlar. İlginç olarak tasarım herhangi bir dile taşınabilir ve ben bunu mobil uygulamalar için de kullanıyorum:
- iOS'ta Swift API Çağrıları
- Android'de Kotlin API Çağrıları
Elbette, kodum geliştirilebilir, örneğin tip dönüştürmeden önce daha iyi doğrulama yapmak - belki servis ajanları bunu yönetebilir. Ama bir yazılım şirketindeki tüm geliştiriciler kodumu anlayabilir, ki bence en önemli sonuç budur.
Cevap #2
Bu cevabı veriyorum ama emin değilim, çünkü ilk sorunuzda bazı bilgiler eksik.
function foo<T extends (...args: any) => any>(fn: T):()=>ReturnType<typeof fn> {
return function bar(): ReturnType<typeof fn>
{
return fn({ /*config*/ });
}
}
const f1 = () => Promise.resolve(({ value: 'value' } ));
const x1 = foo(f1)
const y1 = x1().then(data => data.value); // type of Promise<string> as value is string
const f2 = () => Promise.resolve(({ value:1234 } ));
const x2 = foo(f1)
const y2 = x2().then(data => data.value); // type of Promise<number> as value is string
