Mongoose, how to enforce LeanDocument type?

Eck*_*sDy 5 mongoose typescript typescript-generics nestjs nestjs-mongoose

In our codebase we've been using T.lean() or T.toObject() and our return types would be LeanDocument<T>. Mongoose 7 no longer exports LeanDocument, and the existing migration guide suggests using the following setup:

// Do this instead, no `extends Document`
interface ITest {
  name?: string;
}
const Test = model<ITest>('Test', schema);

// If you need to access the hydrated document type, use the following code
type TestDocument = ReturnType<(typeof Test)['hydrate']>;
Run Code Online (Sandbox Code Playgroud)

But this gives me HydratedDocument that I can get by HydratedDocument<T>, which is not what I want since it has all the document methods on it.
As an alternative I can use just T as my return type, but then any Document<T> is matching T.

I'd like to enforce that the result is a POJO, to prevent documents leaking from our DAL.

How can I achieve that with typescript and mongoose types?

Eck*_*sDy 1

在 mongoose 存储库中提出类似的问题,我决定采用以下方法:

// utils.ts
export type LeanDocument<T> = T & { $locals?: never };
Run Code Online (Sandbox Code Playgroud)

所以在下面的情况下,typescript会提醒我无法返回文档:

async function getById(id: string): Promise<LeanDocument<User>> {
  const user = await UserModel.findById(id);
  return user;
  //       ^ Types of property '$locals' are incompatible.
}
Run Code Online (Sandbox Code Playgroud)

我认为这可以通过制作一个更清晰的类型错误来进一步改进,该错误将按照 的方式进行说明Type error ... "You've forgot to convert to a lean document".,正如我之前在库中看到的那样。
但我还没有找到如何做到这一点:)

编辑

一些打字稿的魔法:

export type LeanDocument<T> = T & T extends { $locals: never }
  ? T
  : 'Please convert the document to a plain object via `.toObject()`';
Run Code Online (Sandbox Code Playgroud)

会导致如下错误:

async function getById(id: string): Promise<LeanDocument<User>> {
  const user = await UserModel.findById(id);
  return user;
  //       ^ Type 'Document<unknown, any, User> & Omit<User & { _id: ObjectId; }, never>'
  // is not assignable to type 
  // '"Please convert the document to a plain object via `.toObject()`"'.ts(2322)
}
Run Code Online (Sandbox Code Playgroud)

编辑2

使用条件类型的类型错误未按预期工作,我已尝试在这个问题中解决它。不幸的是,工作解决方案需要包装函数和断言。