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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 09:45:26