Diego
Diego

Reputation: 73

Property '_id' does not exist on type Pick

I'm using Next.js and I'm trying to query MongoDB with TypeScript and mongoose, but I have a type error.

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
}

I'm getting the following error:

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)

What is it about Pick<>[] that I am doing wrong?

package.json

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

dbConnect() is a function I took from the Next.js examples

Upvotes: 1

Views: 1969

Answers (1)

&#196;lskar
&#196;lskar

Reputation: 2579

This is because you've told the compiler that data could be an array and in that case requires different accesses to singular objects.

findOne does not return an array, findOne only returns a Record<string, T> or null. You should remove the Pick<dbPost, '_id' | 'post' | 'user'>[] from your type union.

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

Now data could still be null so you need to just make sure you aren't accessing properties on null. The whole function:

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 && {
    ...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
 }

To ensure type-correctness also make sure your select matches the fields in your Pick<>.

Upvotes: 1

Related Questions