TypeScript中extends类型关系疑问:联合类型的矛盾判定
问题根源:TypeScript的分布式条件类型
你遇到的所有“矛盾”,本质都是TypeScript的分布式条件类型在起作用——这是TypeScript处理联合类型时的特殊逻辑,很多人会忽略这个细节。
简单来说:当条件类型中extends左边的检查类型是一个裸类型参数(没有被包裹在元组、对象等结构里)时,TypeScript会自动把联合类型拆成单个成员,分别代入条件判断,最后把所有分支的结果合并成新的联合类型。
第一个例子:doesExtend<'a' | 'b', 'a'>('yes')合法的原因
看函数定义:
function doesExtend<A, B>(_: (A extends B ? 'yes' : 'no')) { }
当你传入A = 'a' | 'b'、B = 'a'时:
- 因为
A是裸类型参数且为联合类型,触发分布式逻辑 - TypeScript会把联合拆成两个独立判断:
'a' extends 'a'→ 结果为'yes''b' extends 'a'→ 结果为'no'
- 最终参数的类型变成
'yes' | 'no',所以传入'yes'完全符合类型要求(它是联合类型的成员之一)
第二个例子:WhoExtendsWho<'a' | 'b', 'a'>返回矛盾联合的原因
同样是分布式条件类型在生效:
type WhoExtendsWho<A, B> = A extends B ? B extends A ? 'A extends B, and B extends A' : 'A extends B, but B does not extend A' : B extends A ? 'A does not extend B, but B extends A' : 'A does not extend B, and B does not extend A';
当A = 'a' | 'b',B = 'a'时:
- 把联合成员
'a'代入判断:'a' extends 'a'→ 进入第一个分支- 接着判断
'a' extends 'a'→ 为true,得到结果'A extends B, and B extends A'
- 把联合成员
'b'代入判断:'b' extends 'a'→ 进入第三个分支- 接着判断
'a' extends 'b'→ 为false,得到结果'A does not extend B, and B does not extend A'
- 最后把两个分支的结果合并,就得到了你看到的矛盾联合类型
如何避免分布式行为?
如果你想让联合类型作为整体进行判断,而不是拆分单个成员,可以把检查类型用元组包裹,破坏“裸类型参数”的条件:
修改后的函数:
function doesExtend<A, B>(_: ([A] extends [B] ? 'yes' : 'no')) { } doesExtend<'a' | 'b', 'a'>('yes'); // 现在会报错,符合预期
修改后的类型工具:
type WhoExtendsWho<A, B> = [A] extends [B] ? [B] extends [A] ? 'A extends B, and B extends A' : 'A extends B, but B does not extend A' : [B] extends [A] ? 'A does not extend B, but B extends A' : 'A does not extend B, and B does not extend A'; type X = WhoExtendsWho<'a' | 'b', 'a'>; // 返回"A does not extend B, but B extends A",符合预期
内容的提问来源于stack exchange,提问作者Trident D'Gao
相关产品推荐
相关产品推荐

