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

TypeScript联合类型变量用展开运算符传参时未报类型错误

TypeScript 展开联合类型对象时类型校验失效的原因与规避方法

核心成因

这不是TypeScript的bug,是两个既定规则共同作用的结果:

  • 对象展开传入的属性不会触发对象字面量的多余属性检查。TS的多余属性检查(也就是对象字面量多传不存在的字段时报错的规则)仅对字面量内直接书写的属性生效,通过展开运算符混入的属性不会走这条校验逻辑。
  • 联合类型展开时采用宽松兼容判定。当你展开A | B类型的对象时,TS不会要求展开结果同时满足A和B的约束,只要联合中的任意一个成员类型和目标参数类型不存在必现冲突,就会判定为兼容。你的场景里StripeCreateStoreOptions分支根本不存在shopifyOfflineSession字段,TS会认为「存在传入Stripe类型options的可能,此时展开结果完全符合参数要求」,因此不会抛出错误。
  • 另外两种写法能正常报错的原因很明确:
    • 直接在data里写shopifyOfflineSession字段属于字面量直接声明属性,会触发严格的类型校验和多余属性检查,结构不匹配直接报错。
    • 条件判断返回options.shopifyOfflineSession | undefined时,字段类型是明确的联合类型,TS会直接校验该类型是否和目标字段匹配,存在不兼容的类型分支就会报错。

规避方案

  • 方案1:显式声明中间变量类型,强制触发严格校验
    先把要传入的data对象赋值给显式标注了Prisma对应类型的变量,类型不匹配时TS会直接在变量声明处抛错:
    import type { Prisma } from '@prisma/client';
    
    // 此处会强制校验...options的结果是否符合StoreCreateInput类型
    const createData: Prisma.StoreCreateInput = {
      ...options,
    };
    
    const store = await this.prisma.store.create({
      include: { accounts: true, shopifyOfflineSession: true },
      data: createData,
    });
    
  • 方案2:收窄联合类型后分别构造传参对象
    通过类型守卫区分Stripe、Shopify两种场景,拆分处理字段,从根源上避免直接展开联合类型混入不兼容字段:
    let createData: Prisma.StoreCreateInput;
    if (this.isFromShopify(options)) {
      const { shopifyOfflineSession, ...baseOpts } = options;
      createData = {
        ...baseOpts,
        // 按照Prisma关联字段的要求做结构转换,比如嵌套create
        shopifyOfflineSession: shopifyOfflineSession
          ? { create: shopifyOfflineSession }
          : undefined,
      };
    } else {
      createData = { ...options };
    }
    
  • 方案3:按需开启exactOptionalPropertyTypes TS编译选项,收紧可选字段的兼容规则,注意开启前需要先验证项目存量代码、以及第三方依赖(比如Prisma)的类型兼容性,避免出现大面积误报。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 05:54:26