Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

Confused about payload-types

default discord avatar
nballlast year
16

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
    notchrlast year
    @511305637959827459

    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
    nballlast year

    Good morning

    @1049775120559898725

    ! 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
    notchrlast year

    Yeah it's tough to say why Payload considered a string a potential type, but my bet is still on the depth value. If

    @808734492645785600

    has some time, I think he could clarify the type confusion

  • default discord avatar
    nballlast year

    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
    last year

    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
    last year
    @783701636165402624

    is spot on here

  • default discord avatar
    nballlast year
    @783701636165402624

    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
    paulpopuslast year

    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
    last year

    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
    paulpopuslast year

    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
    last year

    great point

  • default discord avatar
    nballlast year

    Thank you so much! This was very helpful.

  • default discord avatar
    modgy.last year

    Reviving an old thread because I'm unclear on how this works. I'm getting the error

    Property 'artistName' does not exist on type 'string'

    artistName

    does

    exist on the

    Artist

    type



    <ul>
      {listPicks.map(({ id, track, artist, genres }) => (
        <li key={id}>
          {track} - {artist.artistName}
        </li>
      ))}
    </ul>


    Is the solution really to write


    {artist && typeof artist !== "string" && artist.artistName}


    Every time I want to use anything from the Artist type? I guess I could do something like this instead...


    {listPicks.map(({ id, track, artist, genres }) => {
      if (typeof artist === "string") return null;
    
      return (
        <li key={id}>
          {track} - {artist.artistName}
        </li>
      );
    })}
  • default discord avatar
    meda_bbdk6 months ago

    based on this

    https://github.com/payloadcms/payload/discussions/1563#discussioncomment-5357724

    , I modify for my needs,


    const fs = require('fs')
    const path = require('path')
    
    // Function to read and process the payload-types.ts
    function modifyPayloadGenerateTypes() {
      // Define the path to the payload-types.ts file
      const filePath = path.join(__dirname, '../src/payload-types.ts')
    
      // Read the file content
      let fileContent
    
      try {
        fileContent = fs.readFileSync(filePath, 'utf8')
      } catch (error) {
        console.error('Error reading the file:', error)
    
        return
      }
    
      // Replace specific union types for `string | Media`
      const stringMediaRegex = /string \| Media;/g
      const updatedContent = fileContent.replace(stringMediaRegex, 'Media;')
    
      // Write the modified content back to the file
      try {
        console.log('Updating types for string | Media fields...')
        fs.writeFileSync(filePath, updatedContent)
        console.log('Done.')
      } catch (error) {
        console.error('Error writing to the file:', error)
      }
    }
    
    // Run the function to process the types
    modifyPayloadGenerateTypes()


    in package.json


    "generate:types": "cross-env NODE_OPTIONS=--no-deprecation payload generate:types && node scripts/modify-payload-generate-types.cjs"

  • default discord avatar
    ricou26813 weeks ago

    Hey ! Any other way to solve the string | Media issue ?

  • default discord avatar
    paulpopus3 weeks ago

    for now type guarding, we're working on improving this via an sdk



    if (typeof image !== 'string') {}
Star on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

Get dedicated engineering support directly from the Payload team.