案例
多说无益,直接上代码
interface User {
name: string;
id: string;
}
type userListType = Array<User>;
type GetArrayType<T> = T extends Array<infer R> ? R : T;
type T = GetArrayType<userListType>;
根据以上的代码最后 T 为
这是我在学习时作者讲的案例,什么鬼,完全看不懂啊,让我好好分析一下。
首先,我们定义了 User 这个接口 并在 userListType 中使用了它
-
userListType等价于User[] -
userListType是User类型的数组,即User[]
条件运算符 extends...?:
让我们再学习一下 extends…?: 条件运算符这个概念
条件运算符extends...?:可以根据当前类型是否符合某种条件,返回不同的类型。
T extends U ? X : Y
上面式子中的extends用来判断,类型T是否可以赋值给类型U,即T是否为U的子类型,这里的T和U可以是任意类型。
详解
此时我们再看一下这段代码
type GetArrayType<T> = T extends Array<infer R> ? R : T;
type T = GetArrayType<userListType>;
// 我们可以转换一下代码
type T = userListType extends Array<infer R> ? R : T;
- 条件判断:检查
userListType是否满足Array<infer R>(即是否是一个数组类型)。 infer R的作用:如果userListType是数组,则自动推断数组元素类型为R。- 结果分支:
- True 分支:返回
R(即数组元素类型User)。 - False 分支:返回
T(这里会导致循环引用,实际应为其他类型,如never)。
- True 分支:返回
关键点解析
1. extends 条件类型的作用
- 模式匹配:
Array<infer R>是一种类型模式匹配,检查左侧类型是否匹配数组结构。 - 类型推断:通过
infer R动态提取数组元素的类型。
2. 实际推导过程
- 条件成立时:
因为userListType是User[],匹配Array<infer R>,此时R被推断为User。
→T的类型为User。 - 条件不成立时(假设修改为其他情况):
例如,若userListType不是数组,则进入T分支,但由于T引用了自身,会导致 无限递归类型错误。
→ 实际代码中应避免这种设计,通常使用never作为备选类型。
修复循环引用问题
type SafeT = userListType extends Array<infer R> ? R : never;
- 当
userListType是数组:SafeT的类型为User。 - 当
userListType不是数组:SafeT的类型为never(表示不可能存在的情况)。