Mongoose CastError问题:customers数组转字符串失败排查
报错信息
CastError: 在路径"customers"处将值"[ 'ZTM', 'NASA' ]"(类型Array)转换为string失败
at model.Query.exec (/Users/mike/Documents/NodeJS-applications/NASA-project/server/node_modules/mongoose/lib/query.js:4891:21)
at model.Query.Query.then (/Users/mike/Documents/NodeJS-applications/NASA-project/server/node_modules/mongoose/lib/query.js:4990:15)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
错误详情:
{
messageFormat: undefined,
stringValue:"[ 'ZTM', 'NASA' ]",
kind: 'string',
value: [ 'ZTM', 'NASA' ],
path: 'customers',
reason: null,
valueType: 'Array'
}
我推测问题出在Schema中定义的customers字段(类型为字符串数组)。以下是相关代码:
Schema定义(使用Typegoose)
import { getModelForClass, prop, Ref, index } from "@typegoose/typegoose"; import * as mongoose from "mongoose"; import { Planet } from "./planets.typegoose"; @index({ flightNumber: 1 }) class Launch { @prop({ type: () => Number, required: true }) public flightNumber: number; @prop({ type: () => Date, required: true }) public launchDate: Date; @prop({ type: () => String, required: true }) public mission: string; @prop({ type: () => String, required: true }) public rocket: string; @prop({ ref: () => Planet, required: true }) public target: Ref<Planet, mongoose.Types.ObjectId>; @prop({ type: () => [String], required: true }) public customers?: string[]; @prop({ type: () => Boolean, required: true }) public upcoming: boolean; @prop({ type: () => Boolean, required: true, default: true }) public success: boolean; } const LaunchModel = getModelForClass(Launch); export default LaunchModel;
TypeScript接口定义
interface LaunchType { flightNumber?: number; mission: string; rocket: string; launchDate: Date; target: string; customers?: string[]; upcoming?: boolean; success?: boolean; }
创建Launch对象
const launch: LaunchType = { flightNumber: 100, mission: "Kepler Exploration Soran", rocket: "Saturn IS2", launchDate: new Date("December 27, 2030"), target: "kepler-442 b", customers: ["ZTM", "NASA"], upcoming: true, success: true, };
保存到MongoDB的函数
async function saveLaunchToMongoDB(launch: LaunchType): Promise<void> { await LaunchModel.updateOne( { flightNumber: launch.flightNumber, }, launch, { upsert: true } ); }
当我从Schema、接口和Launch对象中移除类型为string[]的customers属性后,可以正常保存对象到MongoDB集合,无任何错误。
问题疑问:是否是因为接口中customers为string[] | undefined联合类型,但Schema中定义为字符串构造函数导致的问题?
问题分析与解决
不是接口的联合类型问题,核心矛盾出在Typegoose的Schema配置上:
- 必填与可选的冲突:
customers字段同时设置了required: true和类属性的可选标记?,这种矛盾配置导致Mongoose解析Schema时出现异常,错误将数组类型判定为单个string类型,进而触发CastError。 - 额外潜在问题:
target字段在Schema中定义为Ref<Planet, mongoose.Types.ObjectId>(关联Planet文档的ObjectId),但创建的launch对象中target是字符串"kepler-442 b",这会导致后续关联查询或保存时出现引用类型错误,需要改为对应Planet文档的ObjectId。
修正方案
方案1:将customers设为可选字段
修改Schema中customers的配置,去掉required: true:
@prop({ type: () => [String] }) public customers?: string[];
TypeScript接口保持customers?: string[]即可,和Schema配置一致。
方案2:将customers设为必填字段
去掉类属性的可选标记?,保持required: true:
@prop({ type: () => [String], required: true }) public customers: string[];
同时修改TypeScript接口,去掉customers的?,确保类型匹配:
interface LaunchType { // ...其他字段 customers: string[]; // ...其他字段 }
修正target字段问题
如果target是关联Planet文档,需要传入对应文档的ObjectId,比如先查询到目标Planet的_id,再赋值给launch对象:
const targetPlanet = await PlanetModel.findOne({ keplerName: "kepler-442 b" }); const launch: LaunchType = { // ...其他字段 target: targetPlanet?._id.toString(), // 或者直接传入ObjectId类型 // ...其他字段 };
内容的提问来源于stack exchange,提问作者Elham Najeebullah

