You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript联合类型窄化行为版本差异及类型推断疑问

关于TypeScript联合类型窄化的版本差异与心智模型疑问

先看你提到的测试代码:

type Name = { name: string }
type Age = { age: number }

type UnionBoth = Name | Age

const test = (union: UnionBoth): void => {
    if ('name' in union && 'age' in union) {
        union.name
        union.age
    }
}

你发现TypeScript 5.4.3中这段代码无报错,但4.8.4版本会提示Property 'name' does not exist on type 'never',同时还有关于类型推断和心智模型的疑问,以下是解答:

一、版本差异的原因

旧版本(如4.8.4)的TypeScript对联合类型的窄化逻辑存在局限:它认为Name | Age的联合成员只能是“仅Name”或“仅Age”,默认排除了“同时属于两者”的可能性。因此当你用'name' in union && 'age' in union做判断时,TypeScript会觉得没有任何值能满足这个条件,所以把代码块内的union推断为never类型,自然报错。

而新版本(5.4.3)改进了联合类型的窄化逻辑,能够正确识别联合类型的成员可以存在交叉(即某个值同时属于多个联合成员的类型),因此会将代码块内的union推断为同时包含name和age的类型,允许访问这两个属性。

二、正确的心智模型

联合类型A | B的本质是:所有属于A、属于B,或者同时属于A和B的值的集合。

用in操作符进行类型窄化时,TypeScript会逐步过滤联合类型的成员:

  1. 当判断'name' in union时,会把联合类型过滤为Name | (Age & { name: unknown })——因为Age本身没有name,但可能存在某个值同时符合Age和带有name的类型;
  2. 接着判断'age' in union,会进一步过滤出同时拥有name和age的类型,最终得到既包含Name的明确属性,又包含未知类型age的交叉类型。

三、为什么推断类型是Name & Record<"age", unknown>

Name类型明确只有name: string属性,而我们通过'age' in union仅能确认age属性存在,但无法确定它的具体类型——毕竟Name | Age的联合类型中,除了Name & Age(即{ name: string, age: number }),还可能存在比如{ name: string, age: boolean }这类同时符合Name和带有任意类型age的对象。

TypeScript用Record<"age", unknown>来表示“存在age属性,但其类型未知”,和Name交叉后,就得到了既有明确name: string、又有未知类型age的类型。

四、你的心智模型是否正确?

完全正确!你的模型精准描述了联合类型的集合本质:联合类型包含“仅Name”“仅Age”“Name & Age”三个子集,通过in操作符的连续判断,确实可以窄化到“同时属于两个类型的交集”这个场景,新版本的TypeScript已经能正确适配这种逻辑。

内容的提问来源于stack exchange,提问作者NeoZoom.lua

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 16:05:09