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

如何在TypeScript中为Mongoose关联填充字段定义类型?

如何为Mongoose中可填充字段定义TypeScript类型?

我有一个Mongoose字段,它既可以是ObjectId,也可以是填充后的完整文档,尝试用TypeScript定义类型时遇到了问题,相关代码如下:

import { InferSchemaType, Schema, Types, model } from 'mongoose';

const personSchema = new Schema({
  _id: Schema.Types.ObjectId,
  name: String,
  age: Number,
});

const storySchema = new Schema({
  author: { type: Schema.Types.ObjectId, ref: 'Person' },
  title: String,
});

export type PersonDao = { _id: Types.ObjectId } & InferSchemaType<typeof personSchema>;
export type StoryDao = { _id: Types.ObjectId; author: Types.ObjectId | PersonDao } & Omit<InferSchemaType<typeof storySchema>, 'author'>;

export const Person = model('Person', personSchema);
export const Story = model('Story', storySchema);

Story.findOne({ _id: 'fakeId' })
  .populate<StoryDao>('author')
  .exec()
  .then((story: StoryDao) => {
    console.log(story.author.name); // 报错
  });

此时访问story.author.name会触发TypeScript错误:

Property 'name' does not exist on type 'ObjectId | PersonDao'.
  Property 'name' does not exist on type 'ObjectId'.ts(2339)

如果把类型定义改成仅允许PersonDao:

export type StoryDao = { _id: Types.ObjectId; author: PersonDao } & Omit<InferSchemaType<typeof storySchema>, 'author'>;

export const story: StoryDao = {
  _id: new Types.ObjectId(),
  author: new Types.ObjectId(), // 报错
  title: 'fakeTitle',
};

name的访问错误消失了,但创建对象时author字段又报错:

type 'ObjectId' is not assignable to type 'PersonDao'.
  Type 'ObjectId' is missing the following properties from type '{ createdAt: NativeDate; updatedAt: NativeDate; }': createdAt, updatedAtts(2322)

这本质是TypeScript联合类型的限制,比如下面的示例同样会报错:

type Test = number | { name: string }
const test: Test = {} as Test;
console.log('DEBUG: ', test.name); // 报错

解决方案

方案1:区分未填充/已填充的类型

分别定义两种类型,对应字段未填充和已填充的状态,让TypeScript能精准推断:

import { InferSchemaType, Schema, Types, model } from 'mongoose';

const personSchema = new Schema({
  _id: Schema.Types.ObjectId,
  name: String,
  age: Number,
});

const storySchema = new Schema({
  author: { type: Schema.Types.ObjectId, ref: 'Person' },
  title: String,
});

// 基础Person类型
export type PersonDao = { _id: Types.ObjectId } & InferSchemaType<typeof personSchema>;
// 未填充author的Story类型
export type StoryDao = { _id: Types.ObjectId } & InferSchemaType<typeof storySchema>;
// 已填充author的Story类型
export type StoryWithAuthorDao = Omit<StoryDao, 'author'> & { author: PersonDao };

export const Person = model('Person', personSchema);
export const Story = model('Story', storySchema);

// 查询并填充后,使用已填充类型
Story.findOne({ _id: 'fakeId' })
  .populate<StoryWithAuthorDao>('author')
  .exec()
  .then((story: StoryWithAuthorDao) => {
    console.log(story.author.name); // 正常无报错
  });

// 创建未填充的Story对象,使用基础类型
const story: StoryDao = {
  _id: new Types.ObjectId(),
  author: new Types.ObjectId(), // 正常无报错
  title: 'fakeTitle',
};

方案2:使用类型守卫判断字段类型

定义类型守卫函数,在运行时判断字段是否为填充后的文档,让TypeScript根据判断结果自动推断类型:

import { InferSchemaType, Schema, Types, model } from 'mongoose';

const personSchema = new Schema({
  _id: Schema.Types.ObjectId,
  name: String,
  age: Number,
});

const storySchema = new Schema({
  author: { type: Schema.Types.ObjectId, ref: 'Person' },
  title: String,
});

export type PersonDao = { _id: Types.ObjectId } & InferSchemaType<typeof personSchema>;
export type StoryDao = { _id: Types.ObjectId; author: Types.ObjectId | PersonDao } & Omit<InferSchemaType<typeof storySchema>, 'author'>;

// 类型守卫函数:判断传入对象是否为PersonDao类型
function isPersonDao(obj: Types.ObjectId | PersonDao): obj is PersonDao {
  return typeof (obj as PersonDao).name !== 'undefined';
}

export const Person = model('Person', personSchema);
export const Story = model('Story', storySchema);

Story.findOne({ _id: 'fakeId' })
  .populate<StoryDao>('author')
  .exec()
  .then((story: StoryDao) => {
    if (isPersonDao(story.author)) {
      console.log(story.author.name); // 类型推断正确,无报错
    } else {
      // 处理author为ObjectId的逻辑
      console.log('Author is an ObjectId:', story.author);
    }
  });

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 22:30:59