如何理解并记住TypeScript中X<T>与条件类型T extends any ? X<T>: X<T>的差异?
为什么TypeScript里
X<T>和T extends any ? X<T> : X<T>不等价? 你肯定会觉得奇怪:明明两个分支的结果一模一样,为什么TypeScript的类型系统会把它们当成不同的东西?这和JavaScript运行时的逻辑完全相反——就像你写了个if-else两个分支都执行同一个函数,和直接调用函数效果完全一样,但在TypeScript类型里,这种看似冗余的条件类型,却有本质区别。
核心原因:条件类型的「自动分发特性」
TypeScript的条件类型有个特殊规则:当条件类型中的泛型参数是「联合类型」时,它会自动把联合类型拆成单个成员,分别计算每个成员的条件结果,最后再把结果合并成新的联合类型。而直接写X<T>的话,只会把整个联合类型当作一个整体传入X处理。
举个最直观的例子:
假设我们定义type X<T> = T[],然后对比两个类型:
type Direct<T> = X<T>:当T是string | number时,结果是(string | number)[]——也就是一个数组里可以同时放字符串和数字。type Conditional<T> = T extends any ? X<T> : X<T>:当T是string | number时,TypeScript会自动拆分联合类型:- 先处理
string extends any ? string[] : string[],得到string[] - 再处理
number extends any ? number[] : number[],得到number[] - 最后把两个结果合并,得到
string[] | number[]——也就是要么全是字符串的数组,要么全是数字的数组,不能混合。
- 先处理
这两个类型的差异一眼就能看出来:(string | number)[]和string[] | number[]完全不是一回事!
怎么快速记住这个差异?
你可以把条件类型看成**「联合类型的自动遍历器」**:
- 直接写
X<T>:把整个联合类型当成“一个打包好的箱子”,直接扔进X里加工。 - 写
T extends any ? X<T> : X<T>:自动把箱子拆开,拿出每个单独的东西加工,再重新打包成新的箱子。
哪怕条件判断的分支完全一样,只要触发了这个自动分发特性,结果就会和直接处理整个联合类型不同。
内容的提问来源于stack exchange,提问作者WBT
相关产品推荐
相关产品推荐

