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.
@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.
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.
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
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.
@tylandavis is spot on here
@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?
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.
Star
Discord
online
Get help straight from the Payload team with an Enterprise License.