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

TypeScript条件对象属性无法生成外部可区分联合的问题

TypeScript类型收窄失效的原因分析

问题重现

开发者期望通过判断state.source === "Batch"来收窄state.items中元素的__typename类型,但第一种写法无法实现:

export type State<T extends "Batch" | "Shipment" = "Batch" | "Shipment"> = {
    source: T
    items: T extends "Batch" ? { __typename: "Batch" }[] : { __typename: "Shipment" }[]
}

const state: State = {
    source: "Batch",
    items: []
}

if (state.source === "Batch") {
    state.items.map((i) => {
        const typename: "Batch" = i.__typename // 报错:类型"Batch" | "Shipment"不能赋值给类型"Batch"
    })
}

将State重构为泛型驱动的联合类型后,类型收窄正常工作:

export type State<T extends "Batch" | "Shipment" = "Batch" | "Shipment"> = T extends "Batch" ? {
    source: T
    items: { __typename: "Batch" }[]
} : {
    source: T
    items: { __typename: "Shipment" } []
}

差异原因

第一种写法:单一对象类型 + 条件类型字段

这种写法中,State是单个对象类型,仅items字段的类型通过泛型T的条件判断计算。当你声明const state: State时,泛型T默认取值"Batch" | "Shipment",最终生成的类型是:

{
    source: "Batch" | "Shipment";
    items: ({ __typename: "Batch" }[] | { __typename: "Shipment" }[]);
}

此时source和items的类型是独立的,TypeScript无法识别二者之间的关联逻辑——它不知道source为"Batch"时,items必须对应{ __typename: "Batch" }[],所以收窄source的类型后,items的类型不会同步收窄。

第二种写法:泛型展开的联合类型

这种写法中,State会根据泛型T的取值展开为多个对象类型的联合。当T为"Batch" | "Shipment"时,最终生成的类型是:

{
    source: "Batch";
    items: { __typename: "Batch" }[];
} | {
    source: "Shipment";
    items: { __typename: "Shipment" }[];
}

这是一个标准的判别式联合类型,source字段是类型判别式。TypeScript可以识别联合类型中每个分支的结构关联,当你判断state.source === "Batch"时,会自动将整个state的类型收窄到对应分支,items的类型也随之确定为{ __typename: "Batch" }[],因此i.__typename可以安全赋值给"Batch"类型。

总结

核心差异在于类型的结构:第一种是带条件字段的单一对象,无法建立字段间的关联约束;第二种是判别式联合类型,每个分支的字段类型强绑定,TypeScript的类型收窄机制可以正常工作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 10:10:18