TypeScript中关于inter的基本用法

104 阅读2分钟

案例

多说无益,直接上代码

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

image.png

这是我在学习时作者讲的案例,什么鬼,完全看不懂啊,让我好好分析一下。

首先,我们定义了 User 这个接口 并在 userListType 中使用了它

  • userListType 等价于 User[]

  • userListType 是 User 类型的数组,即 User[]

条件运算符 extends...?:

让我们再学习一下 extends…?: 条件运算符这个概念

条件运算符extends...?:可以根据当前类型是否符合某种条件,返回不同的类型。

T extends U ? X : Y

上面式子中的extends用来判断,类型T是否可以赋值给类型U,即T是否为U的子类型,这里的TU可以是任意类型。

详解

此时我们再看一下这段代码

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)。

关键点解析

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(表示不可能存在的情况)。