Q&A
Q: 协变和逆变分别是什么?
A: 协变(Covariance)指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型;逆变(Contravariance)指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型。
Q: 协变和逆变有什么好处?
A: 泛型类型参数支持协变和逆变,可在分配和使用泛型类型方面提供更大的灵活性。
协变
假设,现在有三种体型的人,和一件任何人都可以穿的连衣裙
// 普通人
interface People {}
// 高的人
interface TallPeople extends People {
isTall: true
}
// 瘦的人
interface TallAndThinPeople extends TallPeople {
isThin: true
}
const theNormalOne: People = {}
const theTallOne: TallPeople = {
isTall: true
}
const theTallAndThinOne: TallAndThinPeople = {
isTall: true,
isThin: true
}
function onePieceForAllPeople<T extends People>(one: T) {}
onePieceForAllPeople(theNormalOne) // √
显然 TallPeople 是比 People 派生程度更大(更具体)的类型,而 TallAndThinPeople 是比 TallPeople 派生程度更大的类型。
那么,「一个长得高的人,或是一个长得又高又瘦的人 可以去穿一件任何人都可以穿的连衣裙吗?」
onePieceForAllPeople<TallPeople>(theTallOne) // ?
onePieceForAllPeople<TallAndThinPeople>(theTallAndThinOne) // ?
答案当然是没问题的啦!一件任何人都能穿的连衣裙(接受 People 作为参数),当然也允许长得高的人穿(接受比原始指定的类型更具体的类型 TallPeople)。
但是如果这件连衣裙并不是任何人都能穿的,它是一件只有长得高的人才可以穿的裙子呢?
function onePieceForTallPeople<T extends TallPeople>(one: T) {}
// 显然
onePieceForTallPeople<TallPeople>(theTallOne) // √
onePieceForTallPeople<TallAndThinPeople>(theTallAndThinOne) // √
onePieceForTallPeople<People>(theNormalOne) // ×,不满足「长得高」这个要求
// 即 People extends TallPeople 为 false
此时当我们把泛型的要求指定为 TallPeople 的时候,为了方便,我们可以使用 TallPeople 和更具体的子类型 TallAndThinPeople,而 People 不满足要求,这被称之为协变。
逆变
假设现在是在为一位「长得高的人」寻找一件「可以穿的连衣裙」
// 可以写成
type OnePieceForTallPeople = (one: TallPeople) => void
function findOnePiece<T extends OnePieceForTallPeople>(onePiece: T) {}
// 当然也可以直接写成
function findOnePiece<T extends (one: TallPeople) => void>(onePiece: T) {}
// 一件「适合长得高的人穿的连衣裙」当然是符合要求的
// onePieceForTallPeople = (one: TallPeople) => {}
findOnePiece<(one: TallPeople) => void>(onePieceForTallPeople) // √
// 一件「任何人都可以穿的连衣裙」也是符合要求的
// onePieceForAllPeople = (one: People) => {}
findOnePiece<(one: People) => void>(onePieceForAllPeople) // √
// 「适合长得高而且瘦的人穿的连衣裙」是不符合要求的,因为有可能这个人并不瘦...
// onePieceForTallAndThinPeople = (one: TallAndThinPeople) => {}
findOnePiece<(one: TallAndThinPeople) => void>(onePieceForTallAndThinPeople) // ×
显然,此时当我们把泛型的要求指定为 (one: TallPeople) => void 的时候,只有 (one: TallPeople) => void 和 (one: People) => void 满足要求,而 (one: TallAndThinPeople) => void 不满足要求,这被称之为逆变,逆变虽然比较少见,但是其实也是在为了可以灵活使用而产生的一个很自然的结果。
结论
为了使用方便,在不同的情况下程序允许我们能够使用比原始指定的类型更具体或是更泛型的类型,而协变和逆变只是描述这种行为的术语