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

TypeScript中如何基于never类型正确判别联合类型实现类型收窄

问题根源
  • 你对never类型的语义理解有偏差:radius: never 表示该属性必须存在,但值不能是任何合法类型,并不等价于「该属性不存在」。你在TCircle和TSquare中都显式声明了必填的radius字段,对TypeScript来说,两个子类型都必然存在radius属性,只是TSquare的radius永远不可能有合法值,这直接导致TSquare本身是无法构造出合法实例的类型。
  • 类型收窄逻辑不符合预期:当你用typeof shape.radius === "number"做判断时,TS会把radius类型为number的TCircle留在if分支,剩下的TSquare进入else分支。此时else分支中shape.radius的类型是never,而TS中never类型代表不可达的代码路径,不会触发类型校验,因此你写的错误运算不会抛错。
解决方案

推荐两种符合TS类型系统设计的实现方式,都能实现你要的类型判别效果:

方式1:用属性存在性判断收窄(最符合直觉)

不要给不存在的字段声明never类型,直接在子类型中只定义自己拥有的字段,用in操作符判断属性是否存在来收窄类型:

type TShape = {
    id: string;
}

type TCircle = TShape & {
    radius: number;
}

type TSquare = TShape & {
    size: number;
}

function getArea(shape: TCircle | TSquare): number {
    if ('radius' in shape) {
        // 此处shape自动收窄为TCircle,可正常访问radius
        return shape.radius * shape.radius * Math.PI;
    } else {
        // 此处shape自动收窄为TSquare,访问shape.radius会直接抛编译错误
        // 正方形面积正确公式为边长平方
        return shape.size * shape.size;
    }
}

方式2:用可选never实现互斥字段(适合必须保留同名字段的场景)

如果你希望两个子类型都保留radius、size字段的声明来实现严格互斥,需要把不属于当前类型的字段标记为可选never(即字段名?: never),而非必填never。可选never表示该字段可以不传,但一旦传值就必须是never(即不允许传任何合法值),既可以实现字段互斥,也能让类型收窄正常工作:

type TShape = {
    id: string;
}

type TCircle = TShape & {
    radius: number;
    size?: never; // 不允许TCircle存在size属性
}

type TSquare = TShape & {
    radius?: never; // 不允许TSquare存在radius属性
    size: number;
}

function getArea(shape: TCircle | TSquare): number {
    if (typeof shape.radius === "number") {
        // 此处shape收窄为TCircle
        return shape.radius * shape.radius * Math.PI;
    } else {
        // 此处shape收窄为TSquare,shape.radius类型为undefined
        // 写shape.radius * shape.size会触发编译错误,符合预期
        return shape.size * shape.size;
    }
}

注意:你原来else分支写的shape.radius * shape.size本身存在逻辑错误,正方形面积公式应为边长的平方,即shape.size * shape.size,修正类型后TS会帮你捕获这类非法访问错误。

内容的提问来源于stack exchange,提问作者Mario Vernari

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 03:03:22