解决TypeORM中JSONB列引发的_QueryDeepPartialEntity类型赋值错误
在使用TypeORM的项目中,为WaitlistAction实体添加类型为Record<string, unknown>的jsonb列后,出现TypeScript类型赋值错误,且该错误影响了FormResponse、FormResponseItem等其他实体的事务更新方法。
错误信息
@/backend:test: resolver/form.resolver.ts:766:40 - error TS2345: Argument of type 'FormResponseItem' is not assignable to parameter of type '_QueryDeepPartialEntity<FormResponseItem>'. @/backend:test: Types of property 'formResponse' are incompatible. @/backend:test: Type 'FormResponse' is not assignable to type '(() => string) | _QueryDeepPartialEntity<FormResponse> | undefined'. @/backend:test: Type 'FormResponse' is not assignable to type '_QueryDeepPartialEntity<FormResponse>'. @/backend:test: Types of property 'form' are incompatible. @/backend:test: Type 'Form' is not assignable to type '(() => string) | _QueryDeepPartialEntity<Form> | undefined'. @/backend:test: Type 'Form' is not assignable to type '_QueryDeepPartialEntity<Form>'. @/backend:test: Types of property 'responses' are incompatible. @/backend:test: Type 'FormResponse[]' is not assignable to type '(() => string) | _QueryDeepPartialEntity<FormResponse>[] | undefined'. @/backend:test: @/backend:test: 766 .update({ id: item.id }, item);
新增列代码
@Column({ type: "jsonb", nullable: true, }) @Field(() => GraphQLJSONObject, { nullable: true }) public metadata?: Record<string, unknown>;
TypeORM可正常生成数据库迁移文件,但运行测试时会触发上述无关实体的错误,经排查确定由该jsonb列添加导致。现需解答:
- 如何解决TypeORM中JSONB列与嵌套实体类型引发的TypeScript类型赋值错误?是否有适配的模式或类型定义?
- 将列类型从
Record<string, unknown>改为Record<string, any>为何能解决错误?此方法在类型安全与最佳实践层面有何影响?
1. 解决JSONB列与嵌套实体的类型冲突问题
有几种可行的方案来修复这个TypeORM类型推导的问题:
方法一:手动指定更新操作的类型
TypeORM自动生成的_QueryDeepPartialEntity在处理Record<string, unknown>这类严格类型时,递归推导会出现兼容性问题。你可以为目标实体创建自定义的深度Partial类型,或者在更新时显式转换类型:
// 通用的深度Partial类型工具 type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]; }; // 在更新时进行类型转换,绕过自动推导的限制 .update({ id: item.id }, item as unknown as DeepPartial<FormResponseItem>);
方法二:显式配置JSON列的类型处理器
在实体列定义中,使用TypeORM的JsonType来显式指定类型处理逻辑,避免TypeScript自动推导时的冲突:
import { JsonType } from "typeorm"; @Column({ type: "jsonb", nullable: true, transformer: JsonType // 显式指定类型转换器 }) @Field(() => GraphQLJSONObject, { nullable: true }) public metadata?: Record<string, unknown>;
方法三:使用QueryBuilder替代实体更新
如果错误是因为实体间的嵌套关联(比如Form、FormResponse、FormResponseItem之间的循环引用)导致类型推导崩溃,可以改用QueryBuilder来执行更新,绕过实体类型的严格检查:
await getRepository(FormResponseItem) .createQueryBuilder() .update() .set({ /* 只传入需要更新的字段,排除关联对象 */ }) .where("id = :id", { id: item.id }) .execute();
2. 改用Record<string, any>的原理与影响
为什么能解决错误?
TypeScript的unknown是严格的类型安全类型,任何对unknown的操作都需要显式类型断言;而any是弱类型,会完全绕过TypeScript的类型检查。TypeORM的_QueryDeepPartialEntity在递归推导嵌套类型时,遇到Record<string, unknown>会严格校验每个嵌套层级的兼容性,而Record<string, any>会被视为任意类型,不会触发类型不兼容的错误。
类型安全与最佳实践层面的影响
- 弊端:完全丧失类型安全,后续对
metadata字段的读写都不会有类型提示和校验,容易出现拼写错误、类型不匹配等问题,长期维护成本很高。 - 适用场景:仅适合临时快速修复问题,或者
metadata确实存储完全无结构的任意数据、不需要类型约束的场景。从长期代码质量来看,不推荐作为最终解决方案,还是应该采用严格类型的适配方法。
内容的提问来源于stack exchange,提问作者thatguyintech

