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

TypeScript类型错误:unknown无法赋值给string | ObjectId求解决方案

解决TypeScript错误:Argument of type 'unknown' is not assignable to parameter of type 'string | ObjectId'

问题场景

使用Mongoose + TypeScript开发时,执行await recursiveDelete(folder._id);触发类型错误,提示unknown类型无法赋值给string | ObjectId。尝试过用.lean()获取普通对象、自定义类型守卫都没解决。

相关代码

folderSchema.ts

import mongoose, { Document, Schema, model } from "mongoose";

export interface IFolder extends Document {
  name: string;
  owner: mongoose.Types.ObjectId;
  parent: mongoose.Types.ObjectId | null;
  files: mongoose.Types.ObjectId[];
  subfolders: mongoose.Types.ObjectId[];
  sharedWith: mongoose.Types.ObjectId[];
  createdAt: Date;
  updatedAt: Date;
}

const folderSchema = new Schema<IFolder>(
  {
    name: { type: String, required: true },
    owner: { type: Schema.Types.ObjectId, ref: "User", required: true },
    parent: { type: Schema.Types.ObjectId, ref: "Folder", default: null },
    files: [{ type: Schema.Types.ObjectId, ref: "File" }],
    subfolders: [{ type: Schema.Types.ObjectId, ref: "Folder" }],
    sharedWith: [{ type: Schema.Types.ObjectId, ref: "User" }],
  },
  { timestamps: true },
);

export default model<IFolder>("Folder", folderSchema);

index.ts

export { default as Folder } from "./folderSchema.ts";

folderController.ts

import mongoose from "mongoose";
import { Request, Response } from "express";
import { Folder } from "./index.ts";

//Delete a folder
export const deleteFolder = async (req: Request, res: Response) => {
  try {
    const folder = await Folder.findOne({
      _id: req.params.id,
      owner: req.user?.userId,
    });

    if (!folder) {
      return res.status(404).json({ message: "Folder not found" });
    }

    //Using recursion to delete the folders and files within parent folder.
    await recursiveDelete(folder._id); //Line causing error.

    res.json({ message: "Folder and contents deleted successfully" });
  } catch (err) {
    res.status(500).json({ message: "Error deleting folder" });
  }
};

//Recursive function to delete
async function recursiveDelete(folderId: string | mongoose.Types.ObjectId) {
  const folder = await Folder.findById(folderId);

  if (!folder) {
    return;
  }

  //Delete all files in this folder
  await File.deleteMany({ _id: { $in: folder.files } });

  for (let subfolderId of folder.subfolders) {
    await recursiveDelete(subfolderId);
  }

  await Folder.findByIdAndDelete(folderId);
}

问题根源

  1. 核心原因:TypeScript无法识别req.user的类型,导致Folder.findOne()的返回类型被推断为unknown,folder._id自然也变成unknown,不匹配recursiveDelete的参数类型要求。
  2. 次要问题:File模型未导入,运行时会报错;循环调用recursiveDelete(subfolderId)时,Mongoose文档数组的元素类型可能存在推断偏差。

解决方案

1. 给Express Request扩展自定义类型

创建types/express.d.ts文件,明确req.user的结构:

import mongoose from "mongoose";
import { Request } from "express";

declare global {
  namespace Express {
    interface Request {
      user?: {
        userId: string | mongoose.Types.ObjectId;
      };
    }
  }
}

这样TypeScript就能正确解析req.user?.userId的类型,Folder.findOne()的返回值会被正确推断为IFolder | null。

2. 显式指定查询返回类型

如果不想修改全局类型,直接在findOne调用时显式声明返回类型:

const folder = await Folder.findOne<IFolder>({
  _id: req.params.id,
  owner: req.user?.userId,
});

3. 修正子文件夹ID的类型断言

循环处理子文件夹时,给subfolderId添加类型断言,确保匹配参数类型:

for (let subfolderId of folder.subfolders) {
  await recursiveDelete(subfolderId as mongoose.Types.ObjectId);
}

4. 导入缺失的File模型

在folderController.ts顶部添加File的导入(假设File已在index.ts导出):

import { Folder, File } from "./index.ts";

修改后的关键代码片段

export const deleteFolder = async (req: Request, res: Response) => {
  try {
    const folder = await Folder.findOne<IFolder>({
      _id: req.params.id,
      owner: req.user?.userId,
    });

    if (!folder) {
      return res.status(404).json({ message: "Folder not found" });
    }

    await recursiveDelete(folder._id);

    res.json({ message: "Folder and contents deleted successfully" });
  } catch (err) {
    res.status(500).json({ message: "Error deleting folder" });
  }
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 09:13:12