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

如何实现TypeScript自定义类型校验错误与默认类型?

问题

想要创建泛型类型PojoDocument<T>,用于阻止Mongoose Document从数据访问层(DAL)泄漏,确保返回对象为POJO而非Mongoose的Document类型。

最初尝试通过检查T是否绝对不存在$locals属性(所有Mongoose Document均包含该属性)实现:

// 若`T`包含`$locals`属性,则解析为字符串类型与`T`冲突触发错误;若不存在则返回`T`
export type PojoDocument<T> = T & T extends { $locals?: never }
  ? T
  : '请通过`.toObject()`或`.lean()`将文档转换为POJO';

但使用时出现类型错误:

class A {
  async create(dto: CreateUserDto): Promise<PojoDocument<User>> {
    const result = await this.userModel.create(dto);
    return result.toObject();
    // ^ 类型“LeanDocument<User & Document<any, any, User>>”无法赋值给类型“请通过`.toObject()`或`.lean()`将文档转换为POJO”。
  }
}

其中LeanDocument<User & Document<any, any, User>>等价于:

interface ResultToObjectType {
  __v?: any;
  _id?: any;
  name: string;
}

调用create方法时,返回类型显示为Promise<"请通过.toObject()或.lean()将文档转换为POJO">,说明PojoDocument<T>默认匹配了else分支,而非预期的T。

后续尝试加入__v和_id属性校验,问题依旧:

export type PojoDocument<T> = T extends { $locals?: never; __v?: any; _id?: any }
  ? T
  : '请通过`.toObject()`或`.lean()`将文档转换为POJO';

如何正确实现该类型校验?

解决方案

核心问题

原类型的判断逻辑方向错误:T extends { $locals?: never }试图校验T是否不包含$locals属性,但TypeScript的条件类型推断对于无该属性的类型会出现匹配偏差,尤其是当T本身未明确约束$locals时,会误判走错误分支。

正确实现

反转判断逻辑,直接检查T是否包含$locals属性,若包含则返回never触发类型错误,否则返回T:

/**
 * 确保返回值是POJO而非Mongoose Document类型
 * 若出现类型错误,请使用`.toObject()`或`.lean()`将Mongoose文档转换为POJO
 */
export type PojoDocument<T> = T extends { $locals: any } 
  ? never 
  : T;

为什么这个写法有效?

  • Mongoose的Document类型明确包含$locals属性,因此会被匹配到never分支,直接触发类型错误;
  • .toObject()或.lean()返回的POJO类型不包含$locals,会直接返回T,符合预期。

更严格的校验(可选)

如果需要排除所有Mongoose自带的内部属性(如_doc),可以扩展条件:

export type PojoDocument<T> = 
  T extends { $locals?: any } | { _doc?: any } 
  ? never 
  : T;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 13:57:00