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.
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.
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.
Yeah it's tough to say why Payload considered a string a potential type, but my bet is still on the depth value. If
@808734492645785600has some time, I think he could clarify the type confusion
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.
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.
is spot on here
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?
I think in these cases a guard check is what's most recommended in the typescript world
if (typeof post.field === 'string')
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} />
}
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
great point
Thank you so much! This was very helpful.
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>
);
})}
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"
Hey ! Any other way to solve the string | Media issue ?
for now type guarding, we're working on improving this via an sdk
if (typeof image !== 'string') {}
Star
Discord
online
Get dedicated engineering support directly from the Payload team.