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); }
问题根源
- 核心原因:TypeScript无法识别
req.user的类型,导致Folder.findOne()的返回类型被推断为unknown,folder._id自然也变成unknown,不匹配recursiveDelete的参数类型要求。 - 次要问题:
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
相关产品推荐
相关产品推荐

