Confused about payload-types

default discord avatar
nball
2 months ago
12

I have a typical upload type of Media, with the interface description including the optional url, however I get a TS error when trying to use the url in Nextjs. Either I am pathing the image tag wrong, or showing my newb level TS skills.



In VS Code the error is

Property 'url' does not exist on type 'string | Media

leading me to ask why a relationship field includes a type of string. Also, why is the url for uploads optional? Is this a case for using filterOptions?



Posts collection includes relationship to Media:


    {
      name: 'featuredImage',
      label: 'Featured Image',
      type: 'upload',
      relationTo: 'media',
      required: true,
    },

Payload-types (imported into front end also):


export interface Post {
  id: string;
  title: string;
  featuredImage: string | Media;
  content: {
    [k: string]: unknown;
  }[];
  slug?: string;
  publishDate?: string;
  displayDate: string;
  meta?: {
    title?: string;
    description?: string;
    image?: string | Media;
  };
  updatedAt: string;
  createdAt: string;
  _status?: 'draft' | 'published';
}
export interface Media {
  id: string;
  title: string;
  alt: string;
  updatedAt: string;
  createdAt: string;
  url?: string;

In Next.js this yields an error, stating the url and alt properties do not exist on type string. (But they do on Media!)


            <Image
              src={post.featuredImage.url}
              alt={post.featuredImage.alt}
              style={{ objectFit: 'contain' }}
              fill
            />

The API does indeed return those values, so the above works in dev but fails to build.

  • default discord avatar
    notchr
    2 months ago

    @nball Good morning! Have you been regenerating your payload types? This may be part of the issue if they have not been updated. I am not sure why Media is a string, that's a good question. My frist thought, which may be incorrect, is that if the "depth" of the collection is set to 0, the media property would be an ID of the related media document instead of the media itself.

  • default discord avatar
    nball
    2 months ago

    Good morning @notchr! I did catch something minor, where the Posts collection featureImage field should have been relationship, rather than upload, however changing that had no effect. Generate:types still yields

    featuredImage: string | Media;

    the or relationship. Notably this also happens with the SEO plugin (configured for Posts) resulting in

      meta?: {
        title?: string;
        description?: string;
        image?: string | Media;
      };


    As for the depth, I am making a simple query in getStaticProps with no depth params. Like this

    const postQuery = await fetch(
        `${process.env.NEXT_PUBLIC_CMS_URL}/api/posts?where[slug][equals]=${slug}`,
      ).then((res) => res.json());


    Although this is frowned upon, I manually removed the string option from each entry on the front end copy of payload-types, and viola! error gone. I supposed using <Omit … etc> would make more sense, but best would be to understand how the optional type is making it in there.

  • default discord avatar
    notchr
    2 months ago

    Yeah it's tough to say why Payload considered a string a potential type, but my bet is still on the depth value. If @jacobsfletch has some time, I think he could clarify the type confusion

  • default discord avatar
    nball
    2 months ago

    I will look into the depth thing, hadn't considered that.



    I don't think depth has anything to do with it unless filterOptions somehow comes into play with the collection. I don't understand how that would be the case.

  • discord user avatar
    tylandavis
    Payload Team
    2 months ago

    Depth is part of it, but also access. Let's say a user doesn't have access to a certain document, but makes a request for a document with a relation to it. They would only be able to get the

    id

    of the document, which is a string. Without including

    string

    in the type, it would be incorrect.

  • discord user avatar
    jacobsfletch
    Payload Team
    2 months ago

    @tylandavis is spot on here

  • default discord avatar
    nball
    2 months ago

    @tylandavis aha! I had not considered that. So this is more of a TS question then? I suppose I could use Omit to force the type to Media. That smells funny to me. Am I missing something Payload specific here?

  • default discord avatar
    paulpopus
    2 months ago

    I think in these cases a guard check is what's most recommended in the typescript world



    if (typeof post.field === 'string')
  • discord user avatar
    tylandavis
    Payload Team
    2 months ago

    in NextJS i find myself chaining together conditions to narrow it down. a little cumbersome but it keeps it typesafe:


    {post.image
      && typeof post.image !== 'string'
      && post.image.url
      && <Image src={post.image.url} />
    }
  • default discord avatar
    paulpopus
    2 months ago

    Yeah ^ and in the backend you can actually use

    ? :

    and do another await payload.find and then your variable assignment like

    const image

    will always be a Media type

  • discord user avatar
    tylandavis
    Payload Team
    2 months ago

    great point

  • default discord avatar
    nball
    2 months ago

    Thank you so much! This was very helpful.

Open the post
Continue the discussion in Discord
Like what we're doing?
Star us on GitHub!

Star

Connect with the Payload Community on Discord

Discord

online

Can't find what you're looking for?

Get help straight from the Payload team with an Enterprise License.