簡體   English   中英

TypeScript - 如何結合使用 mongoose 填充來定義 model?

[英]TypeScript - How to define model in combination with using mongoose populate?

背景

我在我的 Node.JS 應用程序中使用 mongoose 和 TypeScript。 從數據庫中獲取數據時,我在很多地方使用貓鼬populate

我面臨的問題是我不知道如何鍵入我的模型,以便屬性可以是 ObjectId 或填充來自另一個集合的數據。

我試過的

我嘗試在我的 model 類型定義中使用聯合類型,這似乎是 TypeScript 提供的涵蓋此類內容的東西:

interface User extends Document {
    _id: Types.ObjectId;
    name: string
}

interface Item extends Document {
    _id: Types.ObjectId;

    // Union typing here
    user: Types.ObjectId | User;
}

我的架構僅將屬性定義為帶有 ref 的 ObjectId。

const ItemSchema = new Schema({
    user: { type: Schema.Types.ObjectId, ref: "User", index: true }
})

示例

所以我可能會這樣做:

ItemModel.findById(id).populate("user").then((item: Item) => {
    console.log(item.user.name);
})

這會產生編譯錯誤:

[ts] Property 'name' does not exist on type 'User | ObjectId'.
     Property 'name' does not exist on type 'ObjectId'.

問題

我怎樣才能擁有一個 model 屬性,它可以是 TypeScript 中的兩種類型之一?

您需要使用類型保護來縮小Types.ObjectId | User的類型Types.ObjectId | User Types.ObjectId | UserUser ...

如果你正在處理一個User類,你可以使用這個:

if (item.user instanceof User) {
    console.log(item.user.name);
} else {
    // Otherwise, it is a Types.ObjectId
}

如果您有一個與User匹配的結構,但不是一個類的實例(例如,如果User是一個接口),您將需要一個自定義類型保護:

function isUser(obj: User | any) : obj is User {
    return (obj && obj.name && typeof obj.name === 'string');
}

您可以使用它:

if (isUser(item.user)) {
    console.log(item.user.name);
} else {
    // Otherwise, it is a Types.ObjectId
}

如果您不想為此目的檢查結構,您可以使用有區別的 union

當用戶被填充時,將item.userUser

ItemModel.findById(id).populate("user").then((item: Item) => {
    console.log((<User>item.user).name);
})

您可以使用@types/mongoose庫中的PopulatedDoc類型。 請參閱mongoose.doc

Mongoose 的 TypeScript 綁定導出 PopulatedDoc 類型,可幫助您在 TypeScript 定義中定義填充文檔:

import { Schema, model, Document, PopulatedDoc } from 'mongoose';

// `child` is either an ObjectId or a populated document
interface Parent {
  child?: PopulatedDoc<Child & Document>,
  name?: string
}
const ParentModel = model<Parent>('Parent', new Schema({
  child: { type: 'ObjectId', ref: 'Child' },
  name: String
}));

interface Child {
  name?: string;
}
const childSchema: Schema = new Schema({ name: String });
const ChildModel = model<Child>('Child', childSchema);

ParentModel.findOne({}).populate('child').orFail().then((doc: Parent) => {
  // Works
  doc.child.name.trim();
})

下面是 PopulatedDoc 類型的簡化實現。 它需要 2 個通用參數:填充的文檔類型 PopulatedType 和未填充的類型 RawId。 RawId 默認為 ObjectId。

type PopulatedDoc<PopulatedType, RawId = Types.ObjectId> = PopulatedType | RawId;

您作為開發人員負責在填充和非填充文檔之間強制執行強類型。 下面是一個例子。

ParentModel.findOne({}).populate('child').orFail().then((doc: Parent) => {
  // `doc` doesn't have type information that `child` is populated
  useChildDoc(doc.child);
});

// You can use a function signature to make type checking more strict.
function useChildDoc(child: Child): void {
  console.log(child.name.trim());
}

這是從文檔中提取的。 你可以在這里查看

抱歉來晚了,但這是使用 typescript 填充的官方方式

ItemModel.findById(id).populate<{user: 
{ _id: ObjectId, name: string; email: string} 
}>("user").then((item: Item) => {
    console.log(item.user.name);
})

或者

interface User { user: {_id: ObjectId, name: string; email: string} }

ItemModel.findById(id).populate<User>("user").then((item: Item) => {
    console.log(item.user.name);
})

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM