TypeScript:如何向Firestore提交嵌套对象并校验嵌套字段必填性?
解决Firestore嵌套对象的结构校验与提交问题
你的问题核心在于TypeScript接口仅在编译时提供类型提示,运行时不会自动校验传入数据的结构,而且当前代码既没有正确提取overview的字段,也没有对嵌套对象的必填项做校验,导致可以写入任意内容。下面分步骤解决这个问题:
1. 正确解构并嵌套存入Firestore
首先,你需要在解构时把overview也提取出来,并且明确指定它的必填字段,再一起放入projectObject中。这样即使请求里传了overview的额外字段,也会被过滤掉,只保留你需要的结构。修改后的addProject函数如下:
const addProject = async (req: Request, res: Response, next: NextFunction) => { // 解构主对象和嵌套的overview const { projectName, projectType, country, city, overview } = req.body // 进一步解构overview的必填字段,确保只保留需要的内容 const { mainProject, projectId, m_tool_Id, businessUnitId } = overview || {}; try { const project = firestore.collection('projects').doc() const projectObject = { id: project.id, projectName, projectType, country, city, overview: { mainProject, projectId, m_tool_Id, businessUnitId } } await project.set(projectObject) // 别忘了加await,确保存储完成后再返回响应 res.status(200).send({ status: 'success', message: 'project added successfully', data: projectObject }) } catch (error) { next(error); } }
2. 添加运行时必填项校验
TypeScript的类型检查在编译后就失效了,所以必须手动校验所有必填字段是否存在、类型是否符合要求,否则可能会把undefined或错误类型的数据存入Firestore。
手动校验实现
const addProject = async (req: Request, res: Response, next: NextFunction) => { const { projectName, projectType, country, city, overview } = req.body // 校验主对象的必填字段 const requiredMainFields = ['projectName', 'projectType', 'country', 'city']; const missingMainFields = requiredMainFields.filter(field => !req.body[field]); if (missingMainFields.length > 0) { return res.status(400).send({ status: 'error', message: `缺少必填字段: ${missingMainFields.join(', ')}` }); } // 校验嵌套overview的合法性 if (!overview || typeof overview !== 'object') { return res.status(400).send({ status: 'error', message: 'overview是必填项,且必须是对象类型' }); } const requiredOverviewFields = ['mainProject', 'projectId', 'm_tool_Id', 'businessUnitId']; const missingOverviewFields = requiredOverviewFields.filter(field => !overview[field]); if (missingOverviewFields.length > 0) { return res.status(400).send({ status: 'error', message: `overview中缺少必填字段: ${missingOverviewFields.join(', ')}` }); } // 额外校验projectId的类型是否为数字 if (typeof overview.projectId !== 'number') { return res.status(400).send({ status: 'error', message: 'overview.projectId必须是数字类型' }); } const { mainProject, projectId, m_tool_Id, businessUnitId } = overview; try { const project = firestore.collection('projects').doc() const projectObject = { id: project.id, projectName, projectType, country, city, overview: { mainProject, projectId, m_tool_Id, businessUnitId } } await project.set(projectObject) res.status(200).send({ status: 'success', message: 'project added successfully', data: projectObject }) } catch (error) { next(error); } }
使用Schema校验库简化(推荐)
如果你的项目有大量这类校验需求,手动写代码会很繁琐。可以用Zod这样的库,它能同时定义运行时Schema和生成TypeScript类型,一举两得:
首先安装Zod:
npm install zod
然后定义Schema并校验:
import { z } from 'zod'; // 定义overview的Schema const OverviewSchema = z.object({ mainProject: z.string(), projectId: z.number(), m_tool_Id: z.string(), businessUnitId: z.string() }); // 定义Project的Schema const ProjectSchema = z.object({ projectName: z.string(), projectType: z.string(), country: z.string(), city: z.string(), overview: OverviewSchema }); // 从Schema自动生成TypeScript类型 type Project = z.infer<typeof ProjectSchema>; const addProject = async (req: Request, res: Response, next: NextFunction) => { try { // 自动校验请求体是否符合Schema,不符合会抛出错误 const validatedProject = ProjectSchema.parse(req.body); const project = firestore.collection('projects').doc() const projectObject = { id: project.id, ...validatedProject // 直接展开校验后的对象,确保结构完全符合要求 } await project.set(projectObject) res.status(200).send({ status: 'success', message: 'project added successfully', data: projectObject }) } catch (error) { if (error instanceof z.ZodError) { // 处理校验错误,返回详细的错误信息 return res.status(400).send({ status: 'error', message: '请求体格式错误', errors: error.errors.map(err => `${err.path.join('.')}: ${err.message}`) }); } next(error); } }
关键要点总结
- TypeScript接口≠运行时校验:接口只是编译时的类型约束,运行时必须自己做数据校验,否则无效数据会直接存入数据库。
- 显式解构嵌套字段:只提取需要的字段,避免多余数据进入Firestore,保证数据结构整洁。
- 必填项校验不可少:确保所有必填字段存在且类型正确,避免存入无效数据。
- Schema库提升效率:对于复杂的嵌套结构,Zod等库能大幅减少重复代码,同时保持类型安全。
内容的提问来源于stack exchange,提问作者Paersik
相关产品推荐
相关产品推荐

