TypeScript联合类型变量用展开运算符传参时未报类型错误
TypeScript 展开联合类型对象时类型校验失效的原因与规避方法
核心成因
这不是TypeScript的bug,是两个既定规则共同作用的结果:
- 对象展开传入的属性不会触发对象字面量的多余属性检查。TS的多余属性检查(也就是对象字面量多传不存在的字段时报错的规则)仅对字面量内直接书写的属性生效,通过展开运算符混入的属性不会走这条校验逻辑。
- 联合类型展开时采用宽松兼容判定。当你展开
A | B类型的对象时,TS不会要求展开结果同时满足A和B的约束,只要联合中的任意一个成员类型和目标参数类型不存在必现冲突,就会判定为兼容。你的场景里StripeCreateStoreOptions分支根本不存在shopifyOfflineSession字段,TS会认为「存在传入Stripe类型options的可能,此时展开结果完全符合参数要求」,因此不会抛出错误。 - 另外两种写法能正常报错的原因很明确:
- 直接在data里写
shopifyOfflineSession字段属于字面量直接声明属性,会触发严格的类型校验和多余属性检查,结构不匹配直接报错。 - 条件判断返回
options.shopifyOfflineSession | undefined时,字段类型是明确的联合类型,TS会直接校验该类型是否和目标字段匹配,存在不兼容的类型分支就会报错。
- 直接在data里写
规避方案
- 方案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:按需开启
exactOptionalPropertyTypesTS编译选项,收紧可选字段的兼容规则,注意开启前需要先验证项目存量代码、以及第三方依赖(比如Prisma)的类型兼容性,避免出现大面积误报。
内容的提问来源于stack exchange,提问作者Gezim
相关产品推荐
相关产品推荐

