英文:
Mongoose, how to enforce LeanDocument type?
问题
以下是您要翻译的内容:
在我们的代码库中,我们一直在使用 T.lean()
或 T.toObject()
,我们的返回类型将是 LeanDocument<T>
。Mongoose 7 不再导出 LeanDocument,现有的迁移指南建议使用以下设置:
// 改用这个,不再使用 `extends Document`
interface ITest {
name?: string;
}
const Test = model<ITest>('Test', schema);
// 如果需要访问已填充的文档类型,请使用以下代码
type TestDocument = ReturnType<(typeof Test)['hydrate']>
但这会给我一个 HydratedDocument
,我可以通过 HydratedDocument<T>
获取它,这不是我想要的,因为它包含了所有文档方法。作为替代,我可以只使用 T
作为返回类型,但然后任何 Document<T>
都与 T
匹配。
我希望强制确保结果是一个普通的 JavaScript 对象(POJO),以防止文档从我们的数据访问层(DAL)泄漏。
如何在 TypeScript 和 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']>;
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?
答案1
得分: 1
以下是要翻译的内容:
在mongoose存储库中提出了类似的问题后,我已经采用了以下方法:
// utils.ts
export type LeanDocument<T> = T & { $locals?: never };
因此,在以下情况下,typescript将提醒我不能返回文档:
async function getById(id: string): Promise<LeanDocument<User>> {
const user = await UserModel.findById(id);
return user;
// ^ 属性'$locals'的类型不兼容。
}
我认为可以通过制定更清晰的类型错误来进一步改进,该错误将陈述类似于“类型错误...“您忘记将其转换为精简文档”。正如我之前在库中看到的那样。但是我还没有找到如何做到这一点
编辑
一些typescript魔法:
export type LeanDocument<T> = T & T extends { $locals: never }
? T
: '请通过`.toObject()`将文档转换为普通对象';
将导致以下错误:
async function getById(id: string): Promise<LeanDocument<User>> {
const user = await UserModel.findById(id);
return user;
// ^ 类型'Document<unknown, any, User> & Omit<User & { _id: ObjectId; }, never>'不可分配给类型'"请通过`.toObject()`将文档转换为普通对象"。ts(2322)
}
编辑 2
使用条件类型的类型错误未按预期工作,我尝试在此问题中解决它。不幸的是,有效的解决方案需要包装函数和断言。
英文:
Asking a similar question over at the mongoose repo, I've settled on the following approach:
// utils.ts
export type LeanDocument<T> = T & { $locals?: never };
So in the following case, typescript will remind me that I cannot return document:
async function getById(id: string): Promise<LeanDocument<User>> {
const user = await UserModel.findById(id);
return user;
// ^ Types of property '$locals' are incompatible.
}
I think this can be further improved by making a clearer type error that will state something along the lines of Type error ... "You've forgot to convert to a lean document".
, as I've seen that in libraries before.
<strike>But I haven't found how to do that yet :)</strike>
Edit
Some typescript magic:
export type LeanDocument<T> = T & T extends { $locals: never }
? T
: 'Please convert the document to a plain object via `.toObject()`';
Will result in the following error:
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)
}
Edit 2
The type error using conditional types did not work as expected and I've tried to solve it in this question. Unfortunately the working solution required a wrapping function and assertion.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论