一、 协变和逆变
协变(Covariance)\ 逆变(Contravariance)
核心:描述「子类型」和「父类型」在不同场景下的赋值规则,本质是解决「类型兼容」问题。
先定义基础类型(方便后续示例):
1 | interface Animal { name:string } |
1.协变: 子->父能赋值(顺理成章)
规则:子类型可以赋值给父类型,常见于【返回值、数组、对象属性】等场景。
1 | // 示例1:函数返回值(协变) |
👉 为什么合理?:拿「动物数组」的场景来说,你要一个 “装动物的数组”,给你 “装狗的数组” 完全没问题(狗也是动物)。
2. 逆变(Contravariance):「父→子」能赋值(反直觉但合理)
✅ 规则:父类型可以赋值给子类型,仅存在于「函数参数」场景。
1 | // 示例:函数参数(逆变) |
👉 为什么合理?:feedAnimal 只用到了 Animal 的 name 属性,即使传入 Dog(有 name)也能处理;反过来如果用「接收 Dog 的函数」赋值给「接收 Animal 的变量」,会报错(Animal 没有 breed 属性)。
一句话总结:
- 协变:「更具体的类型」→「更宽泛的类型」(返回值、数组);
- 逆变:「更宽泛的类型」→「更具体的类型」(仅函数参数)。
二、 infer
核心:infer 是「类型推导关键字」,只能用在 extends 条件类型中,作用是「提取类型的某一部分」,相当于 “类型层面的变量”。
1. 基础用法:提取函数返回值类型
1 | // 定义工具类型:提取函数的返回值类型 |
👉 逻辑拆解:
T extends (...args: any[]) => infer R:判断 T 是否是函数类型;- 如果是,用
infer R推导函数的返回值类型,并把它存到 R 里; - 如果不是,返回
never(表示 “不存在的类型”)
2. 进阶用法:提取数组元素类型
1 | // 提取数组的元素类型 |
核心价值:
不用手动写死类型,动态提取复杂类型的某一部分,是 TypeScript 「类型体操」的核心工具。