Typescript: Neden Promise<A | B>, Promise<A> | Promise<B> olarak atanamaz?
type A = Promise<string> | Promise<number>
type B = Promise<string | number>
const a: A = Math.random() > 0.5 ? Promise.resolve('string') : Promise.resolve(1)
const b: B = Promise.resolve(Math.random() > 0.5 ? 'string' : 1)
class C {
fa(a: A) {
console.log(a)
}
fb(b: B) {
console.log(b)
}
}
const c = new C()
c.fa(a)
c.fb(b)
c.fa(b) /* <-- This produces an error:
Argument of type 'B' is not assignable to parameter of type 'A'.
Type 'Promise<string | number>' is not assignable to type 'Promise<string>'.
Type 'string | number' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.ts(2345)
*/
c.fb(a) // This works
Bu kod neden hata veriyor?
Not: Typescript 4.9.4
Cevap #1
Genel olarak, F<T> | F<U>'nin F<T | U> ile eşdeğer olduğu doğru değildir; bunun için F'nin tip argümanında kovaryant olması ve bunun tersinin de doğru olması gerekir (varyansın açıklaması için TypeScript'te Varyans, Kovaryans, Kontravaryans ve Bivaryans arasındaki Farklara bakın). Ve derleyici, bu formlar eşdeğer olsa bile birini diğerine zorla dönüştürmek zorunda değildir.
Özellikle, Promise<T> | Promise<U> ve Promise<T | U> güvenle değiştirilebilir. Peki soru şudur: Neden bu TypeScript'te gerçekleşmiyor?
Yetkili cevabın microsoft/TypeScript#51293'te bulunabileceği anlaşılıyor. TS geliştirici ekibi liderine göre:
Önerilen indirgeme kuralı [
Promise<T> | Promise<U>ilePromise<T | U>eşdeğerliliği] bugün geçerlidir, ama bundan ne yapmam gerektiğinden emin değilim.TC39'un yarın, Promise'lerin geri çağrıyı yeni bir şekilde çalıştıran
.thenAgainmetoduna sahip olması gerektiğine karar vermesini gerçekten engelleyen hiçbir şey yok ve o noktadaPromise<T | U> === Promise<T> | Promise<U>kuralı geçerliliğini yitirecek. O zaman oldukça zor bir durumda oluruz.Bu özel kural, açıkça herkes bir şekilde Promiseler ile etkileşimde bulunmasına rağmen, diğer geri bildirimlerde duymadığımız bir şeydir ve en azından bazı geçerli programları reddetmeyen hiçbir tür denetleyici yok, bu yüzden genel olarak burada bir eylem maddesi görmüyorum.
Yani cevap, 'düşük talep ile potansiyel geleceğe hazırlık sorunlarının birleşimi' gibi görünüyor.
Cevap #2
Hata mesajına göre:
‘string | number’ türü, ‘string’ türüne atanamaz
Bunu küme birleşimleri açısından düşündüğünüzde daha kolay olur.
A, (1)stringdöndüren tüm sözlerin kümesi ile (2)numberdöndürenlerin kümesinin birleşimidir.B,stringveyanumberdöndürebilecek tüm promise’lerin kümesidir.
Biri diğerinin bir alt kümesidir, aynı küme değiller.
Bu şuna benzer:
- Dünyadaki tüm beyin cerrahları + tüm roket mühendisleri.
- Hem beyin cerrahı hem de roket mühendisi olan tüm insanlar.
Şimdi eğer sadece yapabilecekleri işe (beyin ameliyatı yapmak veya fırlatılabilir roketler tasarlamak) önem veriyorsanız, iki grubun üretebileceği olası işin aynı olduğu konusunda haklısınız.
Bu durum burada da geçerlidir:
a.then(x => ...) // (parameter) x: string | number
b.then(y => ...) // (parameter) y: string | number
