Свойство ‘_id’ не существует при выборе типа

#mongodb #typescript #mongoose #next.js

#mongodb #typescript #мангуст #next.js

Вопрос:

Я использую Next.js и я пытаюсь запросить MongoDB с помощью TypeScript и mongoose, но у меня ошибка типа.

types.d.ts

 type dbPost = {
  _id: string
  user: {
    uid: string
    name: string
    avatar: string
  }
  post: {
    title: string
    description: string
    markdown: string
    slug: string
    createdAt: string
  }
}
  
 export const getSlugData = async (slug: string) => {
  await dbConnect()

  const data:
    | Pick<dbPost, '_id' | 'post' | 'user'>
    // The problem seems to be here with Pick[]
    | Pick<dbPost, '_id' | 'post' | 'user'>[]
    | null = await Post.findOne({ 'post.slug': slug }).lean().select('-__v')

  const post = {
    ...data,
    _id: `${data._id}`,
    // _id and createdAt are objects created by mongoose which can't be serialized.
    // They must be converted to a string
    post: {
      ...data.post,
      createdAt: `${data.post.createdAt}`,
    },
  }
  return post
}
  

Я получаю следующую ошибку:

 Property '_id' does not exist on type 'Pick<dbPost, "_id" | "post" | "user"> | Pick<dbPost, "_id" | "post" | "user">[]'.
  Property '_id' does not exist on type 'Pick<dbPost, "_id" | "post" | "user">[]'.ts(2339)
  

Pick<>[] Что я делаю не так?

package.json

   "dependencies": {
    "mongoose": "^5.10.6",
...
  },
  "devDependencies": {
    "@types/mongoose": "^5.7.36",
...
  }
  

DBConnect() — это функция, которую я взял из Next.js примеры

Ответ №1:

Это потому, что вы сообщили компилятору, что data это может быть массив, и в этом случае требуются разные обращения к отдельным объектам.

findOne не возвращает массив, findOne только возвращает Record<string, T> или null . Вы должны удалить Pick<dbPost, '_id' | 'post' | 'user'>[] из вашего объединения типов.

 const data:
| Pick<dbPost, '_id' | 'post' | 'user'>
| null = await Post.findOne({ 'post.slug': slug }).lean().select('-__v')
  

Теперь data все еще может быть null так, что вам нужно просто убедиться, что вы не обращаетесь к свойствам null . Вся функция:

 export const getSlugData = async (slug: string) => {
  await dbConnect()

   const data:
    | Pick<dbPost, '_id' | 'post' | 'user'>
    | null = await Post.findOne({ 'post.slug': slug }).lean().select('-__v')

   // first check if data is null
   const post = data amp;amp; {
    ...data,
    _id: `${data._id}`,
    // _id and createdAt are objects created by mongoose which can't be serialized.
    // They must be converted to a string
      post: {
        ...data.post,
        createdAt: `${data.post.createdAt}`,
      },
    }
    return post
 }
  

Для обеспечения корректности типа также убедитесь, что ваш select соответствует полям в вашем Pick<> .

Комментарии:

1. Правильно, но когда я использую ваше решение, компилятор по-прежнему жалуется на объединение типов: Type 'Pick<any, string | number | symbol> | Pick<any, string | number | symbol>[]' is not assignable to type 'Pick<dbPost, "_id" | "post" | "user">' Type 'Pick<any, string | number | symbol>[]' is missing the following properties from type 'Pick<dbPost, "_id" | "post" | "user">': _id, post, userts(2322)

2. @Diego, возможно, это потому, что вам также нужно добавить .exec() , чтобы фактически выполнить запрос: await Post.findOne({ 'post.slug': slug }).lean().select('-__v').exec()

3. Я пробовал это. Это не изменило ошибку. На данный момент я переместил эту функцию в файл .js. Я мало что знаю, но может возникнуть проблема с объявлением типа мангустом. Я вернусь к этому позже. Спасибо.