MongoDB子文档$set更新时的TypeScript类型错误解决
问题描述
示例文档
{ _id: ObjectId('63e501cc2054071132171098'), name: 'Ricky', discriminator: 7706, registerTime: ISODate('2023-02-09T14:23:08.159Z'), friends: { '63e502f4e196ec7c04c4351e': { friendId: ObjectId('63e502f4e196ec7c04c4351e'), friendshipStatus: null, privateChannelId: ObjectId('63e66dd85eb6c3cc248ecc95'), active: true } } }
需求
将friends.63e502f4e196ec7c04c4351e.friendshipStatus字段从null更新为1,且保留子文档中的friendId、privateChannelId和active属性,不替换整个子文档。
当前尝试代码
const friendUpdateResult = await collections.users!.updateOne( { _id: targetFriend._id, }, { $set: { [`friends.${currentUser._id.toString()}.friendshipStatus`]: null, }, } );
TypeScript错误信息
Type '{ [x: string]: null; }' is not assignable to type 'Readonly<{ [x: `friends.${string}`]: unknown; [x: `friends.${string}.friendId`]: Friend | undefined; [x: `friends.${string}.friendshipStatus`]: Friend | undefined; [x: `friends.${string}.privateChannelId`]: Friend | undefined; [x: `friends.${string}.active`]: Friend | undefined;. 'string' and '`friends.${string}.friendId`' index signatures are incompatible. Type 'null' is not assignable to type 'Friend | undefined'.
TypeScript接口定义
interface User { name: string; discriminator: number; registerTime: Date; friends: Record<string, Friend>; } interface Friend { friendId: ObjectId; friendshipStatus?: FriendshipEnum | null; privateChannelId?: ObjectId; active?: boolean; }
解决方案
错误核心是TypeScript无法正确推断动态生成的字段路径类型,误判为要给friends下的键赋值为null,而非子文档的friendshipStatus字段。以下是几种可行的解决方式:
方式1:显式类型断言
直接给$set对象添加类型断言,明确动态路径对应的字段类型合法:
const friendUpdateResult = await collections.users!.updateOne( { _id: targetFriend._id }, { $set: { [`friends.${currentUser._id.toString()}.friendshipStatus`]: 1 } as { [key: string]: FriendshipEnum | null } } );
方式2:使用MongoDB类型工具约束
借助MongoDB官方的UpdateFilter类型,明确更新操作的类型范围,再用any临时绕过路径推断限制(实际类型符合接口定义):
import { Filter, UpdateFilter } from "mongodb"; const update: UpdateFilter<User> = { $set: { [`friends.${currentUser._id.toString()}.friendshipStatus`]: 1 } as any }; const friendUpdateResult = await collections.users!.updateOne( { _id: targetFriend._id } as Filter<User>, update );
方式3:调整接口索引签名
修改User接口的friends字段定义,让TypeScript能直接识别子字段类型,无需额外断言(适合可调整接口的场景):
interface User { name: string; discriminator: number; registerTime: Date; friends: { [friendId: string]: { friendId: ObjectId; friendshipStatus?: FriendshipEnum | null; privateChannelId?: ObjectId; active?: boolean; }; }; }
内容的提问来源于stack exchange,提问作者Lucas Chan
相关产品推荐
相关产品推荐

